gam7 7.21.2__py3-none-any.whl → 7.22.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gam7 might be problematic. Click here for more details.

gam/__init__.py CHANGED
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
25
25
  """
26
26
 
27
27
  __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
28
- __version__ = '7.21.02'
28
+ __version__ = '7.22.00'
29
29
  __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
30
30
 
31
31
  #pylint: disable=wrong-import-position
@@ -969,14 +969,14 @@ SUSPENDED_CHOICE_MAP = {'notsuspended': False, 'suspended': True}
969
969
  def _getIsSuspended(myarg):
970
970
  if myarg in SUSPENDED_CHOICE_MAP:
971
971
  return SUSPENDED_CHOICE_MAP[myarg]
972
- return getBoolean()
972
+ return getBoolean() #issuspended
973
973
 
974
974
  ARCHIVED_ARGUMENTS = {'notarchived', 'archived', 'isarchived'}
975
975
  ARCHIVED_CHOICE_MAP = {'notarchived': False, 'archived': True}
976
976
  def _getIsArchived(myarg):
977
977
  if myarg in ARCHIVED_CHOICE_MAP:
978
978
  return ARCHIVED_CHOICE_MAP[myarg]
979
- return getBoolean()
979
+ return getBoolean() #isarchived
980
980
 
981
981
  def _getOptionalIsSuspendedIsArchived():
982
982
  isSuspended = isArchived = None
@@ -1549,10 +1549,12 @@ def getOrderBySortOrder(choiceMap, defaultSortOrderChoice='ASCENDING', mapSortOr
1549
1549
  return (getChoice(choiceMap, mapChoice=True),
1550
1550
  getChoice(SORTORDER_CHOICE_MAP, defaultChoice=defaultSortOrderChoice, mapChoice=mapSortOrderChoice))
1551
1551
 
1552
- def orgUnitPathQuery(path, isSuspended):
1552
+ def orgUnitPathQuery(path, isSuspended, isArchived):
1553
1553
  query = "orgUnitPath='{0}'".format(path.replace("'", "\\'")) if path != '/' else ''
1554
1554
  if isSuspended is not None:
1555
1555
  query += f' isSuspended={isSuspended}'
1556
+ if isArchived is not None:
1557
+ query += f' isArchived={isArchived}'
1556
1558
  return query
1557
1559
 
1558
1560
  def makeOrgUnitPathAbsolute(path):
@@ -6247,63 +6249,77 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6247
6249
  _showInvalidEntity(Ent.USER, user)
6248
6250
  if GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY]:
6249
6251
  return entityList
6250
- elif entityType in {Cmd.ENTITY_ALL_USERS, Cmd.ENTITY_ALL_USERS_NS, Cmd.ENTITY_ALL_USERS_NS_SUSP, Cmd.ENTITY_ALL_USERS_SUSP}:
6252
+ elif entityType in Cmd.ALL_USER_ENTITY_TYPES:
6251
6253
  cd = buildGAPIObject(API.DIRECTORY)
6252
- if entityType == Cmd.ENTITY_ALL_USERS and isSuspended is not None:
6253
- query = f'isSuspended={isSuspended}'
6254
+ if entityType == Cmd.ENTITY_ALL_USERS and ((isSuspended is not None) or (isArchived is not None)):
6255
+ if isSuspended is not None:
6256
+ query = f'isSuspended={isSuspended}'
6257
+ if isArchived is not None:
6258
+ query += f' isArchived={isArchived}'
6259
+ else:
6260
+ query = f'isArchived={isArchived}'
6254
6261
  else:
6255
6262
  query = Cmd.ALL_USERS_QUERY_MAP[entityType]
6256
- printGettingAllAccountEntities(Ent.USER)
6263
+ printGettingAllAccountEntities(Ent.USER, query=query)
6257
6264
  try:
6258
6265
  result = callGAPIpages(cd.users(), 'list', 'users',
6259
6266
  pageMessage=getPageMessage(),
6260
6267
  throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
6261
6268
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
6262
- customer=GC.Values[GC.CUSTOMER_ID],
6263
- query=query, orderBy='email', fields='nextPageToken,users(primaryEmail,archived)',
6269
+ customer=GC.Values[GC.CUSTOMER_ID], query=query, orderBy='email',
6270
+ fields='nextPageToken,users(primaryEmail)',
6264
6271
  maxResults=GC.Values[GC.USER_MAX_RESULTS])
6265
6272
  except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
6266
6273
  accessErrorExit(cd)
6267
- entityList = [user['primaryEmail'] for user in result if isArchived is None or isArchived == user['archived']]
6268
- printGotAccountEntities(len(entityList))
6269
- elif entityType in {Cmd.ENTITY_DOMAINS, Cmd.ENTITY_DOMAINS_NS, Cmd.ENTITY_DOMAINS_SUSP}:
6270
- if entityType == Cmd.ENTITY_DOMAINS_NS:
6271
- query = 'isSuspended=False'
6272
- elif entityType == Cmd.ENTITY_DOMAINS_SUSP:
6273
- query = 'isSuspended=True'
6274
- elif isSuspended is not None:
6275
- query = f'isSuspended={isSuspended}'
6274
+ entityList = [user['primaryEmail'] for user in result]
6275
+ elif entityType == Cmd.ENTITY_ALL_USERS_ARCH_OR_SUSP:
6276
+ cd = buildGAPIObject(API.DIRECTORY)
6277
+ for query in ['isSuspended=True', 'isArchived=True']:
6278
+ printGettingAllAccountEntities(Ent.USER, query)
6279
+ try:
6280
+ result = callGAPIpages(cd.users(), 'list', 'users',
6281
+ pageMessage=getPageMessage(),
6282
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
6283
+ retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
6284
+ customer=GC.Values[GC.CUSTOMER_ID], query=query, orderBy='email',
6285
+ fields='nextPageToken,users(primaryEmail)',
6286
+ maxResults=GC.Values[GC.USER_MAX_RESULTS])
6287
+ except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
6288
+ accessErrorExit(cd)
6289
+ entitySet |= {user['primaryEmail'] for user in result}
6290
+ entityList = sorted(list(entitySet))
6291
+ elif entityType in Cmd.DOMAIN_ENTITY_TYPES:
6292
+ if entityType == Cmd.ENTITY_DOMAINS and ((isSuspended is not None) or (isArchived is not None)):
6293
+ if isSuspended is not None:
6294
+ query = f'isSuspended={isSuspended}'
6295
+ if isArchived is not None:
6296
+ query += f' isArchived={isArchived}'
6297
+ else:
6298
+ query = f'isArchived={isArchived}'
6276
6299
  else:
6277
- query = None
6300
+ query = Cmd.DOMAINS_QUERY_MAP[entityType]
6278
6301
  cd = buildGAPIObject(API.DIRECTORY)
6279
6302
  domains = convertEntityToList(entity)
6280
6303
  for domain in domains:
6281
- printGettingAllEntityItemsForWhom(Ent.USER, domain, entityType=Ent.DOMAIN)
6304
+ printGettingAllEntityItemsForWhom(Ent.USER, domain, query=query, entityType=Ent.DOMAIN)
6282
6305
  try:
6283
6306
  result = callGAPIpages(cd.users(), 'list', 'users',
6284
6307
  pageMessage=getPageMessageForWhom(),
6285
6308
  throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND, GAPI.FORBIDDEN],
6286
6309
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
6287
- domain=domain,
6288
- query=query, orderBy='email', fields='nextPageToken,users(primaryEmail,archived)',
6310
+ domain=domain, query=query, orderBy='email',
6311
+ fields='nextPageToken,users(primaryEmail)',
6289
6312
  maxResults=GC.Values[GC.USER_MAX_RESULTS])
6290
6313
  except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden):
6291
6314
  checkEntityDNEorAccessErrorExit(cd, Ent.DOMAIN, domain)
6292
6315
  _incrEntityDoesNotExist(Ent.DOMAIN)
6293
6316
  continue
6294
- entityList = [user['primaryEmail'] for user in result if isArchived is None or isArchived == user['archived']]
6295
- printGotAccountEntities(len(entityList))
6296
- elif entityType in {Cmd.ENTITY_GROUP, Cmd.ENTITY_GROUPS,
6297
- Cmd.ENTITY_GROUP_NS, Cmd.ENTITY_GROUPS_NS,
6298
- Cmd.ENTITY_GROUP_SUSP, Cmd.ENTITY_GROUPS_SUSP,
6299
- Cmd.ENTITY_GROUP_INDE, Cmd.ENTITY_GROUPS_INDE}:
6300
- if entityType in {Cmd.ENTITY_GROUP_NS, Cmd.ENTITY_GROUPS_NS}:
6301
- isSuspended = False
6302
- elif entityType in {Cmd.ENTITY_GROUP_SUSP, Cmd.ENTITY_GROUPS_SUSP}:
6303
- isSuspended = True
6317
+ entityList.extend([user['primaryEmail'] for user in result])
6318
+ elif entityType in Cmd.GROUP_ENTITY_TYPES or entityType in Cmd.GROUPS_ENTITY_TYPES:
6319
+ isArchived, isSuspended = Cmd.GROUPS_QUERY_MAP.get(entityType, (isArchived, isSuspended))
6304
6320
  includeDerivedMembership = entityType in {Cmd.ENTITY_GROUP_INDE, Cmd.ENTITY_GROUPS_INDE}
6305
6321
  cd = buildGAPIObject(API.DIRECTORY)
6306
- groups = convertEntityToList(entity, nonListEntityType=entityType in {Cmd.ENTITY_GROUP, Cmd.ENTITY_GROUP_NS, Cmd.ENTITY_GROUP_SUSP, Cmd.ENTITY_GROUP_INDE})
6322
+ groups = convertEntityToList(entity, nonListEntityType=entityType in Cmd.GROUP_ENTITY_TYPES)
6307
6323
  for group in groups:
6308
6324
  if validateEmailAddressOrUID(group, checkPeople=False):
6309
6325
  group = normalizeEmailAddressOrUID(group)
@@ -6329,11 +6345,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6329
6345
  entityList.append(email)
6330
6346
  else:
6331
6347
  _showInvalidEntity(Ent.GROUP, group)
6332
- elif entityType in {Cmd.ENTITY_GROUP_USERS, Cmd.ENTITY_GROUP_USERS_NS, Cmd.ENTITY_GROUP_USERS_SUSP, Cmd.ENTITY_GROUP_USERS_SELECT}:
6333
- if entityType == Cmd.ENTITY_GROUP_USERS_NS:
6334
- isSuspended = False
6335
- elif entityType == Cmd.ENTITY_GROUP_USERS_SUSP:
6336
- isSuspended = True
6348
+ elif entityType in Cmd.GROUP_USERS_ENTITY_TYPES:
6349
+ isArchived, isSuspended = Cmd.GROUP_USERS_QUERY_MAP.get(entityType, (isArchived, isSuspended))
6337
6350
  cd = buildGAPIObject(API.DIRECTORY)
6338
6351
  groups = convertEntityToList(entity)
6339
6352
  includeDerivedMembership = False
@@ -6424,21 +6437,13 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6424
6437
  _addCIGroupUsersToUsers(name, groupEmail, recursive)
6425
6438
  else:
6426
6439
  _showInvalidEntity(Ent.GROUP, group)
6427
- elif entityType in {Cmd.ENTITY_OU, Cmd.ENTITY_OUS, Cmd.ENTITY_OU_AND_CHILDREN, Cmd.ENTITY_OUS_AND_CHILDREN,
6428
- Cmd.ENTITY_OU_NS, Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OU_AND_CHILDREN_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS,
6429
- Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP}:
6430
- if entityType in {Cmd.ENTITY_OU_NS, Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OU_AND_CHILDREN_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS}:
6431
- isSuspended = False
6432
- elif entityType in {Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP}:
6433
- isSuspended = True
6440
+ elif entityType in Cmd.OU_ENTITY_TYPES or entityType in Cmd.OUS_ENTITY_TYPES:
6441
+ isArchived, isSuspended = Cmd.OU_QUERY_MAP.get(entityType, (isArchived, isSuspended))
6434
6442
  cd = buildGAPIObject(API.DIRECTORY)
6435
- ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in {Cmd.ENTITY_OU, Cmd.ENTITY_OU_AND_CHILDREN,
6436
- Cmd.ENTITY_OU_NS, Cmd.ENTITY_OU_AND_CHILDREN_NS,
6437
- Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP})
6438
- directlyInOU = entityType in {Cmd.ENTITY_OU, Cmd.ENTITY_OUS, Cmd.ENTITY_OU_NS, Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP}
6443
+ ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in Cmd.OU_ENTITY_TYPES)
6444
+ directlyInOU = entityType in Cmd.OU_DIRECT_ENTITY_TYPES
6439
6445
  qualifier = Msg.DIRECTLY_IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT)) if directlyInOU else Msg.IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT))
6440
- fields = 'nextPageToken,users(primaryEmail,orgUnitPath,archived)' if directlyInOU else 'nextPageToken,users(primaryEmail,archived)'
6441
- prevLen = 0
6446
+ fields = 'nextPageToken,users(primaryEmail,orgUnitPath)' if directlyInOU else 'nextPageToken,users(primaryEmail)'
6442
6447
  for ou in ous:
6443
6448
  ou = makeOrgUnitPathAbsolute(ou)
6444
6449
  if ou.startswith('id:'):
@@ -6463,22 +6468,17 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6463
6468
  throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
6464
6469
  GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
6465
6470
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
6466
- customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(ou, isSuspended), orderBy='email',
6471
+ customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(ou, isSuspended, isArchived), orderBy='email',
6467
6472
  fields=fields, maxResults=GC.Values[GC.USER_MAX_RESULTS])
6468
6473
  for users in feed:
6469
6474
  if directlyInOU:
6470
6475
  for user in users:
6471
- if ouLower == user.get('orgUnitPath', '').lower() and (isArchived is None or isArchived == user['archived']):
6476
+ if ouLower == user.get('orgUnitPath', '').lower():
6472
6477
  usersInOU += 1
6473
6478
  entityList.append(user['primaryEmail'])
6474
- elif isArchived is None:
6479
+ else:
6475
6480
  entityList.extend([user['primaryEmail'] for user in users])
6476
6481
  usersInOU += len(users)
6477
- else:
6478
- for user in users:
6479
- if isArchived == user['archived']:
6480
- usersInOU += 1
6481
- entityList.append(user['primaryEmail'])
6482
6482
  setGettingAllEntityItemsForWhom(Ent.USER, ou, qualifier=qualifier)
6483
6483
  printGotEntityItemsForWhom(usersInOU)
6484
6484
  except (GAPI.invalidInput, GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError, GAPI.badRequest,
@@ -6488,7 +6488,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6488
6488
  elif entityType in {Cmd.ENTITY_QUERY, Cmd.ENTITY_QUERIES}:
6489
6489
  cd = buildGAPIObject(API.DIRECTORY)
6490
6490
  queries = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType == Cmd.ENTITY_QUERY)
6491
- prevLen = 0
6492
6491
  for query in queries:
6493
6492
  printGettingAllAccountEntities(Ent.USER, query)
6494
6493
  try:
@@ -6512,9 +6511,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6512
6511
  email not in entitySet):
6513
6512
  entitySet.add(email)
6514
6513
  entityList.append(email)
6515
- totalLen = len(entityList)
6516
- printGotAccountEntities(totalLen-prevLen)
6517
- prevLen = totalLen
6518
6514
  elif entityType == Cmd.ENTITY_LICENSES:
6519
6515
  skusList = []
6520
6516
  for item in entity.split(','):
@@ -6573,8 +6569,7 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6573
6569
  ClientAPIAccessDeniedExit(str(e))
6574
6570
  elif entityType == Cmd.ENTITY_CROS:
6575
6571
  buildGAPIObject(API.DIRECTORY)
6576
- result = convertEntityToList(entity)
6577
- for deviceId in result:
6572
+ for deviceId in convertEntityToList(entity):
6578
6573
  if deviceId not in entitySet:
6579
6574
  entitySet.add(deviceId)
6580
6575
  entityList.append(deviceId)
@@ -6598,7 +6593,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6598
6593
  nonListEntityType=entityType == Cmd.ENTITY_CROS_QUERY)
6599
6594
  if entityType == Cmd.ENTITY_CROS_SN:
6600
6595
  queries = [f'id:{query}' for query in queries]
6601
- prevLen = 0
6602
6596
  for query in queries:
6603
6597
  printGettingAllAccountEntities(Ent.CROS_DEVICE, query)
6604
6598
  try:
@@ -6619,25 +6613,15 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6619
6613
  if deviceId not in entitySet:
6620
6614
  entitySet.add(deviceId)
6621
6615
  entityList.append(deviceId)
6622
- totalLen = len(entityList)
6623
- printGotAccountEntities(totalLen-prevLen)
6624
- prevLen = totalLen
6625
- elif entityType in {Cmd.ENTITY_CROS_OU, Cmd.ENTITY_CROS_OU_AND_CHILDREN, Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN,
6626
- Cmd.ENTITY_CROS_OU_QUERY, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, Cmd.ENTITY_CROS_OUS_QUERY, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERY,
6627
- Cmd.ENTITY_CROS_OU_QUERIES, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES, Cmd.ENTITY_CROS_OUS_QUERIES, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERIES}:
6616
+ elif entityType in Cmd.CROS_OU_ENTITY_TYPES or entityType in Cmd.CROS_OUS_ENTITY_TYPES:
6628
6617
  cd = buildGAPIObject(API.DIRECTORY)
6629
- ous = convertEntityToList(entity, shlexSplit=True,
6630
- nonListEntityType=entityType in {Cmd.ENTITY_CROS_OU, Cmd.ENTITY_CROS_OU_AND_CHILDREN,
6631
- Cmd.ENTITY_CROS_OU_QUERY, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY,
6632
- Cmd.ENTITY_CROS_OU_QUERIES, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES})
6618
+ ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in Cmd.CROS_OU_ENTITY_TYPES)
6633
6619
  numOus = len(ous)
6634
- includeChildOrgunits = entityType in {Cmd.ENTITY_CROS_OU_AND_CHILDREN, Cmd.ENTITY_CROS_OUS_AND_CHILDREN,
6635
- Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERY,
6636
- Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERIES}
6620
+ includeChildOrgunits = entityType in Cmd.CROS_OU_CHILDREN_ENTITY_TYPES
6637
6621
  allQualifier = Msg.DIRECTLY_IN_THE.format(Ent.Choose(Ent.ORGANIZATIONAL_UNIT, numOus)) if not includeChildOrgunits else Msg.IN_THE.format(Ent.Choose(Ent.ORGANIZATIONAL_UNIT, numOus))
6638
- if entityType in {Cmd.ENTITY_CROS_OU_QUERY, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, Cmd.ENTITY_CROS_OUS_QUERY, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERY}:
6622
+ if entityType in Cmd.CROS_OU_QUERY_ENTITY_TYPES:
6639
6623
  queries = getQueries('query')
6640
- elif entityType in {Cmd.ENTITY_CROS_OU_QUERIES, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES, Cmd.ENTITY_CROS_OUS_QUERIES, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERIES}:
6624
+ elif entityType in Cmd.CROS_OU_QUERIES_ENTITY_TYPES:
6641
6625
  queries = getQueries('queries')
6642
6626
  else:
6643
6627
  queries = [None]
@@ -6710,7 +6694,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6710
6694
  customerId = _getCustomerIdNoC()
6711
6695
  queries = convertEntityToList(entity, shlexSplit=entityType == Cmd.ENTITY_BROWSER_QUERIES,
6712
6696
  nonListEntityType=entityType == Cmd.ENTITY_BROWSER_QUERY)
6713
- prevLen = 0
6714
6697
  for query in queries:
6715
6698
  printGettingAllAccountEntities(Ent.CHROME_BROWSER, query)
6716
6699
  try:
@@ -6730,9 +6713,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6730
6713
  if deviceId not in entitySet:
6731
6714
  entitySet.add(deviceId)
6732
6715
  entityList.append(deviceId)
6733
- totalLen = len(entityList)
6734
- printGotAccountEntities(totalLen-prevLen)
6735
- prevLen = totalLen
6736
6716
  else:
6737
6717
  systemErrorExit(UNKNOWN_ERROR_RC, 'getItemsToModify coding error')
6738
6718
  for errorType in [ENTITY_ERROR_DNE, ENTITY_ERROR_INVALID]:
@@ -7045,10 +7025,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed=
7045
7025
  choices += Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES
7046
7026
  entityType = mapEntityType(getChoice(choices), typeMap)
7047
7027
  return (Cmd.ENTITY_USERS if entityType not in Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES else Cmd.ENTITY_CROS,
7048
- getItemsToModify(entityType, getEntitiesFromFile(shlexSplit=entityType in [Cmd.ENTITY_OUS, Cmd.ENTITY_OUS_AND_CHILDREN,
7049
- Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS,
7050
- Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP,
7051
- Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN])))
7028
+ getItemsToModify(entityType, getEntitiesFromFile(shlexSplit=entityType in Cmd.OUS_ENTITY_TYPES | {Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN})))
7052
7029
  if entitySelector == Cmd.ENTITY_SELECTOR_CSVDATAFILE:
7053
7030
  if userAllowed:
7054
7031
  choices += Cmd.USER_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY] else [Cmd.ENTITY_USERS]
@@ -7056,10 +7033,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed=
7056
7033
  choices += Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES
7057
7034
  entityType = mapEntityType(getChoice(choices), typeMap)
7058
7035
  return (Cmd.ENTITY_USERS if entityType not in Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES else Cmd.ENTITY_CROS,
7059
- getItemsToModify(entityType, getEntitiesFromCSVFile(shlexSplit=entityType in [Cmd.ENTITY_OUS, Cmd.ENTITY_OUS_AND_CHILDREN,
7060
- Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS,
7061
- Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP,
7062
- Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN])))
7036
+ getItemsToModify(entityType, getEntitiesFromCSVFile(shlexSplit=entityType in Cmd.OUS_ENTITY_TYPES | {Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN})))
7063
7037
  if entitySelector == Cmd.ENTITY_SELECTOR_CSVKMD:
7064
7038
  if userAllowed:
7065
7039
  choices += Cmd.USER_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY] else [Cmd.ENTITY_USERS]
@@ -7102,10 +7076,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed=
7102
7076
  if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY]:
7103
7077
  buildGAPIObject(API.DIRECTORY)
7104
7078
  if entityClass == Cmd.ENTITY_USERS:
7105
- if entityType in [Cmd.ENTITY_GROUP_USERS,
7106
- Cmd.ENTITY_GROUP_USERS_NS, Cmd.ENTITY_GROUP_USERS_SUSP,
7107
- Cmd.ENTITY_GROUP_USERS_SELECT,
7108
- Cmd.ENTITY_CIGROUP_USERS]:
7079
+ if entityType in Cmd.GROUP_USERS_ENTITY_TYPES | {Cmd.ENTITY_CIGROUP_USERS}:
7109
7080
  # Skip over sub-arguments
7110
7081
  while Cmd.ArgumentsRemaining():
7111
7082
  myarg = getArgument()
@@ -10149,8 +10120,8 @@ def threadBatchWorker(showCmds=False, numItems=0):
10149
10120
  batchWriteStderr(f'{currentISOformatTimeStamp()},{pid}/{numItems},Error,{str(e)},{logCmd}\n')
10150
10121
  GM.Globals[GM.TBATCH_QUEUE].task_done()
10151
10122
 
10152
- BATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD]
10153
- TBATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.EXECUTE_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD]
10123
+ BATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD, Cmd.DATETIME_CMD, Cmd.SET_CMD, Cmd.CLEAR_CMD]
10124
+ TBATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.EXECUTE_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD, Cmd.DATETIME_CMD, Cmd.SET_CMD, Cmd.CLEAR_CMD]
10154
10125
 
10155
10126
  def ThreadBatchGAMCommands(items, showCmds):
10156
10127
  if not items:
@@ -10262,6 +10233,14 @@ def doBatch(threadBatch=False):
10262
10233
  continue
10263
10234
  if argv:
10264
10235
  cmd = argv[0].strip().lower()
10236
+ if cmd == Cmd.DATETIME_CMD:
10237
+ if len(argv) == 2:
10238
+ kwValues['datetime'] = todaysTime().strftime(argv[1])
10239
+ else:
10240
+ writeStderr(f'Command: >>>{Cmd.QuotedArgumentList([argv[0]])}<<< {Cmd.QuotedArgumentList(argv[1:])}\n')
10241
+ writeStderr(f'{ERROR_PREFIX}{Cmd.ARGUMENT_ERROR_NAMES[Cmd.ARGUMENT_INVALID][1]}: {Msg.EXPECTED} <{Cmd.DATETIME_CMD} DateTimeFormat>)>\n')
10242
+ errors += 1
10243
+ continue
10265
10244
  if cmd == Cmd.SET_CMD:
10266
10245
  if len(argv) == 3:
10267
10246
  kwValues[argv[1]] = argv[2]
@@ -13353,7 +13332,7 @@ def getUserOrgUnits(cd, orgUnit, orgUnitId):
13353
13332
  throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
13354
13333
  GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
13355
13334
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
13356
- customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnit, None), orderBy='email',
13335
+ customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnit, None, None), orderBy='email',
13357
13336
  fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
13358
13337
  userOrgUnits = {}
13359
13338
  for user in result:
@@ -17848,10 +17827,29 @@ ORG_FIELD_INFO_ORDER = ['orgUnitId', 'name', 'description', 'parentOrgUnitPath',
17848
17827
  ORG_FIELDS_WITH_CRS_NLS = {'description'}
17849
17828
 
17850
17829
  def _doInfoOrgs(entityList):
17830
+ def _printUsers(entityType, orgUnitPath, isSuspended, isArchived):
17831
+ users = callGAPIpages(cd.users(), 'list', 'users',
17832
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
17833
+ retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
17834
+ customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, isSuspended, isArchived), orderBy='email',
17835
+ fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
17836
+ printEntitiesCount(entityType, None)
17837
+ usersInOU = 0
17838
+ Ind.Increment()
17839
+ orgUnitPath = orgUnitPath.lower()
17840
+ for user in users:
17841
+ if orgUnitPath == user['orgUnitPath'].lower():
17842
+ printKeyValueList([user['primaryEmail']])
17843
+ usersInOU += 1
17844
+ elif showChildren:
17845
+ printKeyValueList([f'{user["primaryEmail"]} (child)'])
17846
+ usersInOU += 1
17847
+ Ind.Decrement()
17848
+ printKeyValueList([Msg.TOTAL_ITEMS_IN_ENTITY.format(Ent.Plural(entityType), Ent.Singular(Ent.ORGANIZATIONAL_UNIT)), usersInOU])
17849
+
17851
17850
  cd = buildGAPIObject(API.DIRECTORY)
17852
17851
  getUsers = True
17853
- isSuspended = None
17854
- entityType = Ent.USER
17852
+ isSuspended = isArchived = None
17855
17853
  showChildren = False
17856
17854
  while Cmd.ArgumentsRemaining():
17857
17855
  myarg = getArgument()
@@ -17859,7 +17857,8 @@ def _doInfoOrgs(entityList):
17859
17857
  getUsers = False
17860
17858
  elif myarg in SUSPENDED_ARGUMENTS:
17861
17859
  isSuspended = _getIsSuspended(myarg)
17862
- entityType = Ent.USER_SUSPENDED if isSuspended else Ent.USER_NOT_SUSPENDED
17860
+ elif myarg in ARCHIVED_ARGUMENTS:
17861
+ isArchived = _getIsArchived(myarg)
17863
17862
  elif myarg in {'children', 'child'}:
17864
17863
  showChildren = True
17865
17864
  else:
@@ -17890,38 +17889,29 @@ def _doInfoOrgs(entityList):
17890
17889
  printKeyValueWithCRsNLs(field, value)
17891
17890
  if getUsers:
17892
17891
  orgUnitPath = result['orgUnitPath']
17893
- users = callGAPIpages(cd.users(), 'list', 'users',
17894
- throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
17895
- retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
17896
- customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, isSuspended), orderBy='email',
17897
- fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
17898
- printEntitiesCount(entityType, None)
17899
- usersInOU = 0
17900
- Ind.Increment()
17901
- orgUnitPath = orgUnitPath.lower()
17902
- for user in users:
17903
- if orgUnitPath == user['orgUnitPath'].lower():
17904
- printKeyValueList([user['primaryEmail']])
17905
- usersInOU += 1
17906
- elif showChildren:
17907
- printKeyValueList([f'{user["primaryEmail"]} (child)'])
17908
- usersInOU += 1
17909
- Ind.Decrement()
17910
- printKeyValueList([Msg.TOTAL_ITEMS_IN_ENTITY.format(Ent.Plural(entityType), Ent.Singular(Ent.ORGANIZATIONAL_UNIT)), usersInOU])
17892
+ if isArchived is None and isSuspended is None:
17893
+ _printUsers(Ent.USER, orgUnitPath, None, None)
17894
+ else:
17895
+ if isArchived is not None:
17896
+ _printUsers(Ent.USER_ARCHIVED if isArchived else Ent.USER_NOT_ARCHIVED, orgUnitPath, None, isArchived)
17897
+ if isSuspended is not None:
17898
+ _printUsers(Ent.USER_SUSPENDED if isSuspended else Ent.USER_NOT_SUSPENDED, orgUnitPath, isSuspended, None)
17911
17899
  Ind.Decrement()
17912
17900
  except (GAPI.invalidInput, GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError):
17913
17901
  entityActionFailedWarning([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], Msg.DOES_NOT_EXIST, i, count)
17914
17902
  except (GAPI.badRequest, GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden):
17915
17903
  checkEntityAFDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
17916
17904
 
17917
- # gam info orgs|ous <OrgUnitEntity> [nousers|notsuspended|suspended] [children|child]
17918
- def doInfoOrgs():
17919
- _doInfoOrgs(getEntityList(Cmd.OB_ORGUNIT_ENTITY, shlexSplit=True))
17920
-
17921
- # gam info org|ou <OrgUnitItem> [nousers|notsuspended|suspended] [children|child]
17905
+ # gam info org|ou <OrgUnitItem>
17906
+ # [nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child]
17922
17907
  def doInfoOrg():
17923
17908
  _doInfoOrgs([getOrgUnitItem()])
17924
17909
 
17910
+ # gam info orgs|ous <OrgUnitEntity>
17911
+ # [nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child]
17912
+ def doInfoOrgs():
17913
+ _doInfoOrgs(getEntityList(Cmd.OB_ORGUNIT_ENTITY, shlexSplit=True))
17914
+
17925
17915
  ORG_ARGUMENT_TO_FIELD_MAP = {
17926
17916
  'blockinheritance': 'blockInheritance',
17927
17917
  'inheritanceblocked': 'blockInheritance',
@@ -18151,7 +18141,7 @@ def doPrintOrgs():
18151
18141
  return
18152
18142
  if showUserCounts:
18153
18143
  for orgUnit in orgUnits:
18154
- userCounts[orgUnit['orgUnitPath']] = [0, 0]
18144
+ userCounts[orgUnit['orgUnitPath']] = {'suspended': [0, 0], 'archived': [0, 0], 'total': 0}
18155
18145
  qualifier = Msg.IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT))
18156
18146
  printGettingAllEntityItemsForWhom(Ent.USER, orgUnitPath, qualifier=qualifier, entityType=Ent.ORGANIZATIONAL_UNIT)
18157
18147
  pageMessage = getPageMessageForWhom()
@@ -18161,12 +18151,14 @@ def doPrintOrgs():
18161
18151
  throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
18162
18152
  GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
18163
18153
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
18164
- customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None), orderBy='email',
18165
- fields='nextPageToken,users(orgUnitPath,suspended)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
18154
+ customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None, None), orderBy='email',
18155
+ fields='nextPageToken,users(orgUnitPath,suspended,archived)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
18166
18156
  for users in feed:
18167
18157
  for user in users:
18168
18158
  if user['orgUnitPath'] in userCounts:
18169
- userCounts[user['orgUnitPath']][user['suspended']] += 1
18159
+ userCounts[user['orgUnitPath']]['suspended'][user['suspended']] += 1
18160
+ userCounts[user['orgUnitPath']]['archived'][user['archived']] += 1
18161
+ userCounts[user['orgUnitPath']]['total'] += 1
18170
18162
  except (GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.invalidInput, GAPI.badRequest, GAPI.backendError,
18171
18163
  GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden):
18172
18164
  checkEntityDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
@@ -18233,15 +18225,34 @@ def doPrintOrgs():
18233
18225
  (maxCrOSCounts != -1 and total > maxCrOSCounts)):
18234
18226
  continue
18235
18227
  if showUserCounts:
18236
- row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}NotSuspended'] = userCounts[orgUnitPath][0]
18237
- row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Suspended'] = userCounts[orgUnitPath][1]
18238
- row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Total'] = total = userCounts[orgUnitPath][0]+userCounts[orgUnitPath][1]
18228
+ row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}NotArchived'] = userCounts[orgUnitPath]['archived'][0]
18229
+ row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Archived'] = userCounts[orgUnitPath]['archived'][1]
18230
+ row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}NotSuspended'] = userCounts[orgUnitPath]['suspended'][0]
18231
+ row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Suspended'] = userCounts[orgUnitPath]['suspended'][1]
18232
+ row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Total'] = total = userCounts[orgUnitPath]['total']
18239
18233
  if ((minUserCounts != -1 and total < minUserCounts) or
18240
18234
  (maxUserCounts != -1 and total > maxUserCounts)):
18241
18235
  continue
18242
18236
  csvPF.WriteRowTitles(row)
18243
18237
  else:
18244
18238
  csvPF.WriteRow(row)
18239
+ if showCrOSCounts or showUserCounts:
18240
+ crosTitles = []
18241
+ userTitles = []
18242
+ allTitles = csvPF.titlesList[:]
18243
+ for title in allTitles:
18244
+ if showCrOSCounts and title.startswith(f'CrOS{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}'):
18245
+ crosTitles.append(title)
18246
+ csvPF.RemoveTitles(title)
18247
+ if showUserCounts and title.startswith(f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}'):
18248
+ userTitles.append(title)
18249
+ csvPF.RemoveTitles(title)
18250
+ if showCrOSCounts:
18251
+ csvPF.AddTitles(sorted(crosTitles))
18252
+ if showUserCounts:
18253
+ for title in ['NotArchived', 'Archived', 'NotSuspended', 'Suspended', 'Total']:
18254
+ csvPF.AddTitles(f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{title}')
18255
+ csvPF.SetSortTitles([])
18245
18256
  csvPF.writeCSVfile('Orgs')
18246
18257
 
18247
18258
  # gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [Boolean>]]
@@ -18451,7 +18462,7 @@ def doCheckOrgUnit():
18451
18462
  throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
18452
18463
  GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
18453
18464
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
18454
- customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None),
18465
+ customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None, None),
18455
18466
  fields='nextPageToken,users(orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
18456
18467
  for users in feed:
18457
18468
  for user in users:
@@ -18872,7 +18883,7 @@ def makeUserGroupDomainQueryFilters(kwargsDict):
18872
18883
  kwargsQueries.append((kwargs, query))
18873
18884
  return kwargsQueries
18874
18885
 
18875
- def userFilters(kwargs, query, orgUnitPath, isSuspended):
18886
+ def userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived):
18876
18887
  queryTitle = ''
18877
18888
  if kwargs.get('domain'):
18878
18889
  queryTitle += f'domain={kwargs["domain"]}, '
@@ -18891,6 +18902,12 @@ def userFilters(kwargs, query, orgUnitPath, isSuspended):
18891
18902
  else:
18892
18903
  query += ' '
18893
18904
  query += f'isSuspended={isSuspended}'
18905
+ if isArchived is not None:
18906
+ if query is None:
18907
+ query = ''
18908
+ else:
18909
+ query += ' '
18910
+ query += f'isArchived={isArchived}'
18894
18911
  if query is not None:
18895
18912
  queryTitle += f'query="{query}", '
18896
18913
  if queryTitle:
@@ -18902,7 +18919,7 @@ def userFilters(kwargs, query, orgUnitPath, isSuspended):
18902
18919
  # [limittoou <OrgUnitItem>])
18903
18920
  # [user|users <EmailAddressList>] [group|groups <EmailAddressList>]
18904
18921
  # [select <UserTypeEntity>]
18905
- # [issuspended <Boolean>] [aliasmatchpattern <REMatchPattern>]
18922
+ # [issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
18906
18923
  # [shownoneditable] [nogroups] [nousers]
18907
18924
  # [onerowpertarget] [delimiter <Character>]
18908
18925
  # [suppressnoaliasrows]
@@ -18948,7 +18965,7 @@ def doPrintAliases():
18948
18965
  groups = []
18949
18966
  users = []
18950
18967
  aliasMatchPattern = re.compile(r'^.*$')
18951
- isSuspended = orgUnitPath = None
18968
+ isArchived = isSuspended = orgUnitPath = None
18952
18969
  addCSVData = {}
18953
18970
  delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
18954
18971
  while Cmd.ArgumentsRemaining():
@@ -18972,8 +18989,10 @@ def doPrintAliases():
18972
18989
  pass
18973
18990
  elif myarg == 'select':
18974
18991
  _, users = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
18975
- elif myarg == 'issuspended':
18976
- isSuspended = getBoolean()
18992
+ elif myarg in SUSPENDED_ARGUMENTS:
18993
+ isSuspended = _getIsSuspended(myarg)
18994
+ elif myarg in ARCHIVED_ARGUMENTS:
18995
+ isArchived = _getIsArchived(myarg)
18977
18996
  elif myarg in {'user','users'}:
18978
18997
  users.extend(convertEntityToList(getString(Cmd.OB_EMAIL_ADDRESS_LIST, minLen=0)))
18979
18998
  elif myarg in {'group', 'groups'}:
@@ -19008,7 +19027,7 @@ def doPrintAliases():
19008
19027
  for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
19009
19028
  kwargs = kwargsQuery[0]
19010
19029
  query = kwargsQuery[1]
19011
- query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended)
19030
+ query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived)
19012
19031
  printGettingAllAccountEntities(Ent.USER, pquery)
19013
19032
  try:
19014
19033
  entityList = callGAPIpages(cd.users(), 'list', 'users',
@@ -43947,6 +43966,7 @@ def getUserAttributes(cd, updateCmd, noUid=False):
43947
43966
  invalidArgumentExit(Cmd.OB_SCHEMA_NAME_FIELD_NAME)
43948
43967
 
43949
43968
  parameters = {
43969
+ 'notifyRecoveryEmail': False,
43950
43970
  'verifyNotInvitable': False,
43951
43971
  'createIfNotFound': False,
43952
43972
  'noActionIfAlias': False,
@@ -43963,7 +43983,7 @@ def getUserAttributes(cd, updateCmd, noUid=False):
43963
43983
  body = {'name': {'givenName': UNKNOWN, 'familyName': UNKNOWN}}
43964
43984
  body['primaryEmail'] = getEmailAddress(noUid=noUid)
43965
43985
  notFoundBody = {}
43966
- notify = {'subject': '', 'message': '', 'html': False, 'charset': UTF8, 'password': ''}
43986
+ notify = {'recipients': [], 'subject': '', 'message': '', 'html': False, 'charset': UTF8, 'password': ''}
43967
43987
  primary = {}
43968
43988
  updatePrimaryEmail = {}
43969
43989
  groupOrgUnitMap = None
@@ -43975,7 +43995,9 @@ def getUserAttributes(cd, updateCmd, noUid=False):
43975
43995
  while Cmd.ArgumentsRemaining():
43976
43996
  myarg = getArgument()
43977
43997
  if myarg == 'notify':
43978
- notify['recipients'] = getNormalizedEmailAddressEntity(shlexSplit=True, noLower=True)
43998
+ notify['recipients'].extend(getNormalizedEmailAddressEntity(shlexSplit=True, noLower=True))
43999
+ elif myarg == 'notifyrecoveryemail':
44000
+ parameters['notifyRecoveryEmail'] = True
43979
44001
  elif myarg == 'subject':
43980
44002
  notify['subject'] = getString(Cmd.OB_STRING)
43981
44003
  elif myarg in SORF_MSG_FILE_ARGUMENTS:
@@ -44391,7 +44413,7 @@ def createUserAddAliases(cd, user, aliasList, i, count):
44391
44413
  # (groups [<GroupRole>] [[delivery] <DeliverySetting>] <GroupEntity>)*
44392
44414
  # [alias|aliases <EmailAddressList>]
44393
44415
  # [license <SKUID> [product|productid <ProductID>]]
44394
- # [notify <EmailAddressList>
44416
+ # [[notify <EmailAddressList>] [notifyrecoveryemail]
44395
44417
  # [subject <String>]
44396
44418
  # [notifypassword <String>]
44397
44419
  # [from <EmailAaddress>]
@@ -44409,7 +44431,7 @@ def doCreateUser():
44409
44431
  suffix = 0
44410
44432
  originalEmail = body['primaryEmail']
44411
44433
  atLoc = originalEmail.find('@')
44412
- fields = '*' if tagReplacements['subs'] else 'primaryEmail,name'
44434
+ fields = '*' if tagReplacements['subs'] else 'primaryEmail,name,recoveryEmail'
44413
44435
  while True:
44414
44436
  user = body['primaryEmail']
44415
44437
  if parameters['verifyNotInvitable']:
@@ -44450,7 +44472,9 @@ def doCreateUser():
44450
44472
  createUserAddToGroups(cd, result['primaryEmail'], addGroups, 0, 0)
44451
44473
  if addAliases:
44452
44474
  createUserAddAliases(cd, result['primaryEmail'], addAliases, 0, 0)
44453
- if notify.get('recipients'):
44475
+ if (notify.get('recipients') or (parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'))):
44476
+ if parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'):
44477
+ notify['recipients'].append(result['recoveryEmail'])
44454
44478
  sendCreateUpdateUserNotification(result, notify, tagReplacements)
44455
44479
  for productSku in parameters[LICENSE_PRODUCT_SKUIDS]:
44456
44480
  productId = productSku[0]
@@ -44493,7 +44517,7 @@ def verifyUserPrimaryEmail(cd, user, createIfNotFound, i, count):
44493
44517
  # [createifnotfound] [notfoundpassword (random [<Integer>])|blocklogin|<Password>]
44494
44518
  # (groups [<GroupRole>] [[delivery] <DeliverySetting>] <GroupEntity>)*
44495
44519
  # [alias|aliases <EmailAddressList>]
44496
- # [notify <EmailAddressList>
44520
+ # [[notify <EmailAddressList>] [notifyrecoveryemail]
44497
44521
  # [subject <String>]
44498
44522
  # [notifypassword <String>]
44499
44523
  # [from <EmailAaddress>]
@@ -44522,7 +44546,7 @@ def updateUsers(entityList):
44522
44546
  else:
44523
44547
  checkImmutableOUs = False
44524
44548
  i, count, entityList = getEntityArgument(entityList)
44525
- fields = '*' if tagReplacements['subs'] else 'primaryEmail,name'
44549
+ fields = '*' if tagReplacements['subs'] else 'primaryEmail,name,recoveryEmail'
44526
44550
  for user in entityList:
44527
44551
  i += 1
44528
44552
  user = userKey = normalizeEmailAddressOrUID(user)
@@ -44598,7 +44622,10 @@ def updateUsers(entityList):
44598
44622
  entityActionPerformed([Ent.USER, user], i, count)
44599
44623
  if PwdOpts.filename and PwdOpts.password:
44600
44624
  writeFile(PwdOpts.filename, f'{userKey},{PwdOpts.password}\n', mode='a', continueOnError=True)
44601
- if parameters['notifyOnUpdate'] and notify.get('recipients') and notify['password']:
44625
+ if (parameters['notifyOnUpdate'] and notify['password'] and
44626
+ (notify.get('recipients') or (parameters['notifyRecoveryEmail'] and result.get('recoveryEmail')))):
44627
+ if parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'):
44628
+ notify['recipients'].append(result['recoveryEmail'])
44602
44629
  sendCreateUpdateUserNotification(result, notify, tagReplacements, i, count, createMessage=False)
44603
44630
  break
44604
44631
  except GAPI.conditionNotMet as e:
@@ -44632,8 +44659,10 @@ def updateUsers(entityList):
44632
44659
  createUserAddToGroups(cd, result['primaryEmail'], addGroups, i, count)
44633
44660
  if addAliases:
44634
44661
  createUserAddAliases(cd, result['primaryEmail'], addAliases, i, count)
44635
- if notify.get('recipients'):
44662
+ if (notify.get('recipients') or (parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'))):
44636
44663
  notify['password'] = notify['notFoundPassword']
44664
+ if parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'):
44665
+ notify['recipients'].append(result['recoveryEmail'])
44637
44666
  sendCreateUpdateUserNotification(result, notify, tagReplacements, i, count)
44638
44667
  except GAPI.duplicate:
44639
44668
  duplicateAliasGroupUserWarning(cd, [Ent.USER, body['primaryEmail']], i, count)
@@ -45680,7 +45709,7 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
45680
45709
  # [userview] [basic|full|allfields | <UserFieldName>* | fields <UserFieldNameList>]
45681
45710
  # [delimiter <Character>] [sortheaders] [formatjson [quotechar <Character>]] [quoteplusphonenumbers]
45682
45711
  # [convertcrnl]
45683
- # [issuspended <Boolean>] [aliasmatchpattern <REMatchPattern>]
45712
+ # [issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
45684
45713
  # [showitemcountonly]
45685
45714
  # [showvalidcolumn] (addcsvdata <FieldName> <String>)*
45686
45715
  #
@@ -45693,7 +45722,7 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
45693
45722
  # [userview] [basic|full|allfields | <UserFieldName>* | fields <UserFieldNameList>]
45694
45723
  # [delimiter <Character>] [sortheaders] [formatjson [quotechar <Character>]] [quoteplusphonenumbers]
45695
45724
  # [convertcrnl]
45696
- # [issuspended <Boolean>] [aliasmatchpattern <REMatchPattern>]
45725
+ # [issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
45697
45726
  # [showitemcountonly]
45698
45727
  # [showvalidcolumn] (addcsvdata <FieldName> <String>)*
45699
45728
  #
@@ -45701,13 +45730,13 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
45701
45730
  # ([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
45702
45731
  # [limittoou <OrgUnitItem>] [deleted_only|only_deleted])|[select <UserTypeEntity>]
45703
45732
  # [formatjson [quotechar <Character>]] [countonly]
45704
- # [issuspended <Boolean>] [aliasmatchpattern <REMatchPattern>]
45733
+ # [issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
45705
45734
  # [showitemcountonly]
45706
45735
  # [showvalidcolumn] (addcsvdata <FieldName> <String>)*
45707
45736
  #
45708
45737
  # gam <UserTypeEntity> print users [todrive <ToDriveAttribute>*]
45709
45738
  # [formatjson [quotechar <Character>]] [countonly]
45710
- # [issuspended <Boolean>]
45739
+ # [issuspended <Boolean>] [isarchived <Boolean>]
45711
45740
  # [showitemcountonly]
45712
45741
  def doPrintUsers(entityList=None):
45713
45742
  def _writeUserEntity(userEntity):
@@ -45863,7 +45892,7 @@ def doPrintUsers(entityList=None):
45863
45892
  schemaParms = _initSchemaParms('basic')
45864
45893
  projectionSet = False
45865
45894
  oneLicensePerRow = quotePlusPhoneNumbers = showDeleted = False
45866
- aliasMatchPattern = isSuspended = orgUnitPath = orgUnitPathLower = orderBy = sortOrder = None
45895
+ aliasMatchPattern = isArchived = isSuspended = orgUnitPath = orgUnitPathLower = orderBy = sortOrder = None
45867
45896
  viewType = 'admin_view'
45868
45897
  delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
45869
45898
  showValidColumn = ''
@@ -45882,8 +45911,10 @@ def doPrintUsers(entityList=None):
45882
45911
  showDeleted = True
45883
45912
  elif entityList is None and myarg == 'select':
45884
45913
  _, entityList = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
45885
- elif myarg == 'issuspended':
45886
- isSuspended = getBoolean()
45914
+ elif myarg in SUSPENDED_ARGUMENTS:
45915
+ isSuspended = _getIsSuspended(myarg)
45916
+ elif myarg in ARCHIVED_ARGUMENTS:
45917
+ isArchived = _getIsArchived(myarg)
45887
45918
  elif myarg == 'orderby':
45888
45919
  orderBy, sortOrder = getOrderBySortOrder(USERS_ORDERBY_CHOICE_MAP)
45889
45920
  elif myarg == 'userview':
@@ -45996,7 +46027,7 @@ def doPrintUsers(entityList=None):
45996
46027
  for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
45997
46028
  kwargs = kwargsQuery[0]
45998
46029
  query = kwargsQuery[1]
45999
- query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended)
46030
+ query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived)
46000
46031
  printGettingAllAccountEntities(Ent.USER, pquery)
46001
46032
  pageMessage = getPageMessage(showFirstLastItems=True)
46002
46033
  try:
@@ -46006,9 +46037,9 @@ def doPrintUsers(entityList=None):
46006
46037
  GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN,
46007
46038
  GAPI.UNKNOWN_ERROR, GAPI.FAILED_PRECONDITION],
46008
46039
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS+[GAPI.UNKNOWN_ERROR, GAPI.FAILED_PRECONDITION],
46009
- query=query, fields=fields,
46010
46040
  showDeleted=showDeleted, orderBy=orderBy, sortOrder=sortOrder, viewType=viewType,
46011
46041
  projection=schemaParms['projection'], customFieldMask=schemaParms['customFieldMask'],
46042
+ query=query, fields=fields,
46012
46043
  maxResults=maxResults, **kwargs)
46013
46044
  for users in feed:
46014
46045
  if orgUnitPath is None:
@@ -54960,7 +54991,7 @@ def getDriveFileAttribute(myarg, body, parameters, updateCmd):
54960
54991
  elif myarg == 'timestamp':
54961
54992
  parameters[DFA_TIMESTAMP] = getBoolean()
54962
54993
  elif myarg == 'timeformat':
54963
- parameters[DFA_TIMEFORMAT] = getString(Cmd.OB_STRING, minLen=0)
54994
+ parameters[DFA_TIMEFORMAT] = getString(Cmd.OB_DATETIME_FORMAT, minLen=0)
54964
54995
  elif getDriveFileCopyAttribute(myarg, body, parameters):
54965
54996
  pass
54966
54997
  else:
@@ -59547,7 +59578,7 @@ createReturnItemMap = {
59547
59578
  # [(localfile <FileName>|-)|(url <URL>)]
59548
59579
  # [(drivefilename|newfilename <DriveFileName>) | (replacefilename <REMatchPattern> <RESubstitution>)*]
59549
59580
  # [stripnameprefix <String>]
59550
- # [timestamp <Boolean>]] [timeformat <String>]
59581
+ # [timestamp <Boolean>]] [timeformat <DateTimeFormat>]
59551
59582
  # <DriveFileCreateAttribute>* [noduplicate]
59552
59583
  # [(csv [todrive <ToDriveAttribute>*] (addcsvdata <FieldName> <String>)*)) |
59553
59584
  # (returnidonly|returnlinkonly|returneditlinkonly|showdetails)]
@@ -60074,7 +60105,7 @@ def checkDriveFileShortcut(users):
60074
60105
  # [(localfile <FileName>|-)|(url <URL>)]
60075
60106
  # [retainname | (newfilename <DriveFileName>) | (replacefilename <REMatchPattern> <RESubstitution>)*]
60076
60107
  # [stripnameprefix <String>]
60077
- # [timestamp <Boolean>]] [timeformat <String>]
60108
+ # [timestamp <Boolean>]] [timeformat <DateTimeFormat>]
60078
60109
  # <DriveFileUpdateAttribute>*
60079
60110
  # [(gsheet|csvsheet <SheetEntity> [clearfilter])|(addsheet <String>)]
60080
60111
  # [charset <String>] [columndelimiter <Character>]