resources, buildings and features to resource.py

This commit is contained in:
Jay Lee
2020-03-09 20:15:19 -04:00
parent 587fbadd7c
commit b41a6b1d60
6 changed files with 515 additions and 437 deletions

View File

@@ -75,6 +75,8 @@ import controlflow
import display
import fileutils
import gapi.calendar
import gapi.directory
import gapi.directory.resource
import gapi.errors
import gapi.storage
import gapi.vault
@@ -268,8 +270,6 @@ def splitEmailAddress(emailAddress):
return (emailAddress.lower(), GC_Values[GC_DOMAIN].lower())
return (emailAddress[:atLoc].lower(), emailAddress[atLoc+1:].lower())
UID_PATTERN = re.compile(r'u?id: ?(.+)', re.IGNORECASE)
# Normalize user/group email address/uid
# uid:12345abc -> 12345abc
# foo -> foo@domain
@@ -6394,7 +6394,7 @@ def getUserAttributes(i, cd, updateCmd):
location['area'] = sys.argv[i+1]
i += 2
elif myopt in ['building', 'buildingid']:
location['buildingId'] = _getBuildingByNameOrId(cd, sys.argv[i+1])
location['buildingId'] = gapi.directory.resources.getBuildingByNameOrId(cd, sys.argv[i+1])
i += 2
elif myopt in ['desk', 'deskcode']:
location['deskCode'] = sys.argv[i+1]
@@ -7601,231 +7601,6 @@ def doCreateOrg():
parent = body['parentOrgUnitPath']
gapi.call(cd.orgunits(), 'insert', customerId=GC_Values[GC_CUSTOMER_ID], body=body)
def _getBuildingAttributes(args, body={}):
i = 0
while i < len(args):
myarg = args[i].lower().replace('_', '')
if myarg == 'id':
body['buildingId'] = args[i+1]
i += 2
elif myarg == 'name':
body['buildingName'] = args[i+1]
i += 2
elif myarg in ['lat', 'latitude']:
if 'coordinates' not in body:
body['coordinates'] = {}
body['coordinates']['latitude'] = args[i+1]
i += 2
elif myarg in ['long', 'lng', 'longitude']:
if 'coordinates' not in body:
body['coordinates'] = {}
body['coordinates']['longitude'] = args[i+1]
i += 2
elif myarg == 'description':
body['description'] = args[i+1]
i += 2
elif myarg == 'floors':
body['floorNames'] = args[i+1].split(',')
i += 2
else:
controlflow.invalid_argument_exit(myarg, "gam create|update building")
return body
def doCreateBuilding():
cd = buildGAPIObject('directory')
body = {'floorNames': ['1'],
'buildingId': str(uuid.uuid4()),
'buildingName': sys.argv[3]}
body = _getBuildingAttributes(sys.argv[4:], body)
print(f'Creating building {body["buildingId"]}...')
gapi.call(cd.resources().buildings(), 'insert',
customer=GC_Values[GC_CUSTOMER_ID], body=body)
def _makeBuildingIdNameMap(cd):
buildings = gapi.get_all_pages(cd.resources().buildings(), 'list', 'buildings',
customer=GC_Values[GC_CUSTOMER_ID],
fields='nextPageToken,buildings(buildingId,buildingName)')
GM_Globals[GM_MAP_BUILDING_ID_TO_NAME] = {}
GM_Globals[GM_MAP_BUILDING_NAME_TO_ID] = {}
for building in buildings:
GM_Globals[GM_MAP_BUILDING_ID_TO_NAME][building['buildingId']] = building['buildingName']
GM_Globals[GM_MAP_BUILDING_NAME_TO_ID][building['buildingName']] = building['buildingId']
def _getBuildingByNameOrId(cd, which_building, minLen=1):
if not which_building or (minLen == 0 and which_building in ['id:', 'uid:']):
if minLen == 0:
return ''
controlflow.system_error_exit(3, 'Building id/name is empty')
cg = UID_PATTERN.match(which_building)
if cg:
return cg.group(1)
if GM_Globals[GM_MAP_BUILDING_NAME_TO_ID] is None:
_makeBuildingIdNameMap(cd)
# Exact name match, return ID
if which_building in GM_Globals[GM_MAP_BUILDING_NAME_TO_ID]:
return GM_Globals[GM_MAP_BUILDING_NAME_TO_ID][which_building]
# No exact name match, check for case insensitive name matches
which_building_lower = which_building.lower()
ci_matches = []
for buildingName, buildingId in GM_Globals[GM_MAP_BUILDING_NAME_TO_ID].items():
if buildingName.lower() == which_building_lower:
ci_matches.append({'buildingName': buildingName, 'buildingId': buildingId})
# One match, return ID
if len(ci_matches) == 1:
return ci_matches[0]['buildingId']
# No or multiple name matches, try ID
# Exact ID match, return ID
if which_building in GM_Globals[GM_MAP_BUILDING_ID_TO_NAME]:
return which_building
# No exact ID match, check for case insensitive id match
for buildingId in GM_Globals[GM_MAP_BUILDING_ID_TO_NAME]:
# Match, return ID
if buildingId.lower() == which_building_lower:
return buildingId
# Multiple name matches
if len(ci_matches) > 1:
message = 'Multiple buildings with same name:\n'
for building in ci_matches:
message += f' Name:{building["buildingName"]} id:{building["buildingId"]}\n'
message += '\nPlease specify building name by exact case or by id.'
controlflow.system_error_exit(3, message)
# No matches
else:
controlflow.system_error_exit(3, f'No such building {which_building}')
def _getBuildingNameById(cd, buildingId):
if GM_Globals[GM_MAP_BUILDING_ID_TO_NAME] is None:
_makeBuildingIdNameMap(cd)
return GM_Globals[GM_MAP_BUILDING_ID_TO_NAME].get(buildingId, 'UNKNOWN')
def doUpdateBuilding():
cd = buildGAPIObject('directory')
buildingId = _getBuildingByNameOrId(cd, sys.argv[3])
body = _getBuildingAttributes(sys.argv[4:])
print(f'Updating building {buildingId}...')
gapi.call(cd.resources().buildings(), 'patch',
customer=GC_Values[GC_CUSTOMER_ID], buildingId=buildingId, body=body)
def doGetBuildingInfo():
cd = buildGAPIObject('directory')
buildingId = _getBuildingByNameOrId(cd, sys.argv[3])
building = gapi.call(cd.resources().buildings(), 'get',
customer=GC_Values[GC_CUSTOMER_ID], buildingId=buildingId)
if 'buildingId' in building:
building['buildingId'] = f'id:{building["buildingId"]}'
if 'floorNames' in building:
building['floorNames'] = ','.join(building['floorNames'])
if 'buildingName' in building:
sys.stdout.write(building.pop('buildingName'))
display.print_json(building)
def doDeleteBuilding():
cd = buildGAPIObject('directory')
buildingId = _getBuildingByNameOrId(cd, sys.argv[3])
print(f'Deleting building {buildingId}...')
gapi.call(cd.resources().buildings(), 'delete',
customer=GC_Values[GC_CUSTOMER_ID], buildingId=buildingId)
def _getFeatureAttributes(args, body={}):
i = 0
while i < len(args):
myarg = args[i].lower().replace('_', '')
if myarg == 'name':
body['name'] = args[i+1]
i += 2
else:
controlflow.invalid_argument_exit(myarg, "gam create|update feature")
return body
def doCreateFeature():
cd = buildGAPIObject('directory')
body = _getFeatureAttributes(sys.argv[3:])
print(f'Creating feature {body["name"]}...')
gapi.call(cd.resources().features(), 'insert',
customer=GC_Values[GC_CUSTOMER_ID], body=body)
def doUpdateFeature():
# update does not work for name and name is only field to be updated
# if additional writable fields are added to feature in the future
# we'll add support for update as well as rename
cd = buildGAPIObject('directory')
oldName = sys.argv[3]
body = {'newName': sys.argv[5:]}
print(f'Updating feature {oldName}...')
gapi.call(cd.resources().features(), 'rename',
customer=GC_Values[GC_CUSTOMER_ID], oldName=oldName,
body=body)
def doDeleteFeature():
cd = buildGAPIObject('directory')
featureKey = sys.argv[3]
print(f'Deleting feature {featureKey}...')
gapi.call(cd.resources().features(), 'delete',
customer=GC_Values[GC_CUSTOMER_ID], featureKey=featureKey)
def _getResourceCalendarAttributes(cd, args, body={}):
i = 0
while i < len(args):
myarg = args[i].lower().replace('_', '')
if myarg == 'name':
body['resourceName'] = args[i+1]
i += 2
elif myarg == 'description':
body['resourceDescription'] = args[i+1].replace('\\n', '\n')
i += 2
elif myarg == 'type':
body['resourceType'] = args[i+1]
i += 2
elif myarg in ['building', 'buildingid']:
body['buildingId'] = _getBuildingByNameOrId(cd, args[i+1], minLen=0)
i += 2
elif myarg in ['capacity']:
body['capacity'] = getInteger(args[i+1], myarg, minVal=0)
i += 2
elif myarg in ['feature', 'features']:
features = args[i+1].split(',')
body['featureInstances'] = []
for feature in features:
body['featureInstances'].append({'feature': {'name': feature}})
i += 2
elif myarg in ['floor', 'floorname']:
body['floorName'] = args[i+1]
i += 2
elif myarg in ['floorsection']:
body['floorSection'] = args[i+1]
i += 2
elif myarg in ['category']:
body['resourceCategory'] = args[i+1].upper()
if body['resourceCategory'] == 'ROOM':
body['resourceCategory'] = 'CONFERENCE_ROOM'
i += 2
elif myarg in ['uservisibledescription', 'userdescription']:
body['userVisibleDescription'] = args[i+1]
i += 2
else:
controlflow.invalid_argument_exit(args[i], "gam create|update resource")
return body
def doCreateResourceCalendar():
cd = buildGAPIObject('directory')
body = {'resourceId': sys.argv[3],
'resourceName': sys.argv[4]}
body = _getResourceCalendarAttributes(cd, sys.argv[5:], body)
print(f'Creating resource {body["resourceId"]}...')
gapi.call(cd.resources().calendars(), 'insert',
customer=GC_Values[GC_CUSTOMER_ID], body=body)
def doUpdateResourceCalendar():
cd = buildGAPIObject('directory')
resId = sys.argv[3]
body = _getResourceCalendarAttributes(cd, sys.argv[4:])
# Use patch since it seems to work better.
# update requires name to be set.
gapi.call(cd.resources().calendars(), 'patch',
customer=GC_Values[GC_CUSTOMER_ID], calendarResourceId=resId, body=body,
fields='')
print(f'updated resource {resId}')
def doUpdateUser(users, i):
cd = buildGAPIObject('directory')
if users is None:
@@ -8950,18 +8725,6 @@ def doGetAliasInfo(alias_email=None):
print(f' Group Email: {result["email"]}')
print(f' Unique ID: {result["id"]}')
def doGetResourceCalendarInfo():
cd = buildGAPIObject('directory')
resId = sys.argv[3]
resource = gapi.call(cd.resources().calendars(), 'get',
customer=GC_Values[GC_CUSTOMER_ID], calendarResourceId=resId)
if 'featureInstances' in resource:
resource['features'] = ', '.join([a_feature['feature']['name'] for a_feature in resource.pop('featureInstances')])
if 'buildingId' in resource:
resource['buildingName'] = _getBuildingNameById(cd, resource['buildingId'])
resource['buildingId'] = f'id:{resource["buildingId"]}'
display.print_json(resource)
def _filterTimeRanges(activeTimeRanges, startDate, endDate):
if startDate is None and endDate is None:
return activeTimeRanges
@@ -9692,13 +9455,6 @@ def doDeleteAlias(alias_email=None):
if not is_user or (not is_user and not is_group):
gapi.call(cd.groups().aliases(), 'delete', groupKey=alias_email, alias=alias_email)
def doDeleteResourceCalendar():
resId = sys.argv[3]
cd = buildGAPIObject('directory')
print(f'Deleting resource calendar {resId}')
gapi.call(cd.resources().calendars(), 'delete',
customer=GC_Values[GC_CUSTOMER_ID], calendarResourceId=resId)
def doDeleteOrg():
cd = buildGAPIObject('directory')
name = getOrgUnitItem(sys.argv[3])
@@ -11062,178 +10818,6 @@ def doShowLicenses():
line += f'{u_license[i]}: {u_license[i+1]}, '
print(line[:-2])
RESCAL_DFLTFIELDS = ['id', 'name', 'email',]
RESCAL_ALLFIELDS = ['id', 'name', 'email', 'description', 'type', 'buildingid', 'category', 'capacity',
'features', 'floor', 'floorsection', 'generatedresourcename', 'uservisibledescription',]
RESCAL_ARGUMENT_TO_PROPERTY_MAP = {
'description': ['resourceDescription'],
'building': ['buildingId',],
'buildingid': ['buildingId',],
'capacity': ['capacity',],
'category': ['resourceCategory',],
'email': ['resourceEmail'],
'feature': ['featureInstances',],
'features': ['featureInstances',],
'floor': ['floorName',],
'floorname': ['floorName',],
'floorsection': ['floorSection',],
'generatedresourcename': ['generatedResourceName',],
'id': ['resourceId'],
'name': ['resourceName'],
'type': ['resourceType'],
'userdescription': ['userVisibleDescription',],
'uservisibledescription': ['userVisibleDescription',],
}
def doPrintFeatures():
to_drive = False
cd = buildGAPIObject('directory')
titles = []
csvRows = []
fieldsList = ['name']
fields = 'nextPageToken,features(%s)'
possible_fields = {}
for pfield in cd._rootDesc['schemas']['Feature']['properties']:
possible_fields[pfield.lower()] = pfield
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == 'todrive':
to_drive = True
i += 1
elif myarg == 'allfields':
fields = None
i += 1
elif myarg in possible_fields:
fieldsList.append(possible_fields[myarg])
i += 1
elif 'feature'+myarg in possible_fields:
fieldsList.append(possible_fields['feature'+myarg])
i += 1
else:
controlflow.invalid_argument_exit(sys.argv[i], "gam print features")
if fields:
fields = fields % ','.join(fieldsList)
features = gapi.get_all_pages(cd.resources().features(), 'list', 'features',
customer=GC_Values[GC_CUSTOMER_ID], fields=fields)
for feature in features:
feature.pop('etags', None)
feature.pop('etag', None)
feature.pop('kind', None)
feature = utils.flatten_json(feature)
for item in feature:
if item not in titles:
titles.append(item)
csvRows.append(feature)
display.sort_csv_titles('name', titles)
display.write_csv_file(csvRows, titles, 'Features', to_drive)
def doPrintBuildings():
to_drive = False
cd = buildGAPIObject('directory')
titles = []
csvRows = []
fieldsList = ['buildingId']
# buildings.list() currently doesn't support paging
# but should soon, attempt to use it now so we
# won't break when it's turned on.
fields = 'nextPageToken,buildings(%s)'
possible_fields = {}
for pfield in cd._rootDesc['schemas']['Building']['properties']:
possible_fields[pfield.lower()] = pfield
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == 'todrive':
to_drive = True
i += 1
elif myarg == 'allfields':
fields = None
i += 1
elif myarg in possible_fields:
fieldsList.append(possible_fields[myarg])
i += 1
# Allows shorter arguments like "name" instead of "buildingname"
elif 'building'+myarg in possible_fields:
fieldsList.append(possible_fields['building'+myarg])
i += 1
else:
controlflow.invalid_argument_exit(sys.argv[i], "gam print buildings")
if fields:
fields = fields % ','.join(fieldsList)
buildings = gapi.get_all_pages(cd.resources().buildings(), 'list', 'buildings',
customer=GC_Values[GC_CUSTOMER_ID], fields=fields)
for building in buildings:
building.pop('etags', None)
building.pop('etag', None)
building.pop('kind', None)
if 'buildingId' in building:
building['buildingId'] = f'id:{building["buildingId"]}'
if 'floorNames' in building:
building['floorNames'] = ','.join(building['floorNames'])
building = utils.flatten_json(building)
for item in building:
if item not in titles:
titles.append(item)
csvRows.append(building)
display.sort_csv_titles('buildingId', titles)
display.write_csv_file(csvRows, titles, 'Buildings', to_drive)
def doPrintResourceCalendars():
cd = buildGAPIObject('directory')
todrive = False
fieldsList = []
fieldsTitles = {}
titles = []
csvRows = []
query = None
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == 'todrive':
todrive = True
i += 1
elif myarg == 'query':
query = sys.argv[i+1]
i += 2
elif myarg == 'allfields':
fieldsList = []
fieldsTitles = {}
titles = []
for field in RESCAL_ALLFIELDS:
display.add_field_to_csv_file(field, RESCAL_ARGUMENT_TO_PROPERTY_MAP, fieldsList, fieldsTitles, titles)
i += 1
elif myarg in RESCAL_ARGUMENT_TO_PROPERTY_MAP:
display.add_field_to_csv_file(myarg, RESCAL_ARGUMENT_TO_PROPERTY_MAP, fieldsList, fieldsTitles, titles)
i += 1
else:
controlflow.invalid_argument_exit(sys.argv[i], "gam print resources")
if not fieldsList:
for field in RESCAL_DFLTFIELDS:
display.add_field_to_csv_file(field, RESCAL_ARGUMENT_TO_PROPERTY_MAP, fieldsList, fieldsTitles, titles)
fields = f'nextPageToken,items({",".join(set(fieldsList))})'
if 'buildingId' in fieldsList:
display.add_field_to_csv_file('buildingName', {'buildingName': ['buildingName',]}, fieldsList, fieldsTitles, titles)
printGettingAllItems('Resource Calendars', None)
page_message = gapi.got_total_items_first_last_msg('Resource Calendars')
resources = gapi.get_all_pages(cd.resources().calendars(), 'list', 'items',
page_message=page_message, message_attribute='resourceId',
customer=GC_Values[GC_CUSTOMER_ID], query=query,
fields=fields)
for resource in resources:
if 'featureInstances' in resource:
resource['featureInstances'] = ','.join([a_feature['feature']['name'] for a_feature in resource.pop('featureInstances')])
if 'buildingId' in resource:
resource['buildingName'] = _getBuildingNameById(cd, resource['buildingId'])
resource['buildingId'] = f'id:{resource["buildingId"]}'
resUnit = {}
for field in fieldsList:
resUnit[fieldsTitles[field]] = resource.get(field, '')
csvRows.append(resUnit)
display.sort_csv_titles(['resourceId', 'resourceName', 'resourceEmail'], titles)
display.write_csv_file(csvRows, titles, 'Resources', todrive)
def shlexSplitList(entity, dataDelimiter=' ,'):
lexer = shlex.shlex(entity, posix=True)
lexer.whitespace = dataDelimiter
@@ -12340,7 +11924,7 @@ def ProcessGAMCommand(args):
elif argument in ['org', 'ou']:
doCreateOrg()
elif argument == 'resource':
doCreateResourceCalendar()
gapi.directory.resource.createResourceCalendar()
elif argument in ['verify', 'verification']:
doSiteVerifyShow()
elif argument == 'schema':
@@ -12370,9 +11954,9 @@ def ProcessGAMCommand(args):
elif argument in ['export', 'vaultexport']:
gapi.vault.createExport()
elif argument in ['building']:
doCreateBuilding()
gapi.directory.resource.createBuilding()
elif argument in ['feature']:
doCreateFeature()
gapi.directory.resource.createFeature()
elif argument in ['alertfeedback']:
doCreateAlertFeedback()
elif argument in ['gcpfolder']:
@@ -12398,7 +11982,7 @@ def ProcessGAMCommand(args):
elif argument in ['ou', 'org']:
doUpdateOrg()
elif argument == 'resource':
doUpdateResourceCalendar()
gapi.directory.resource.updateResourceCalendar()
elif argument == 'cros':
doUpdateCros()
elif argument == 'mobile':
@@ -12426,9 +12010,9 @@ def ProcessGAMCommand(args):
elif argument in ['project', 'projects', 'apiproject']:
doUpdateProjects()
elif argument in ['building']:
doUpdateBuilding()
gapi.directory.resource.updateBuilding()
elif argument in ['feature']:
doUpdateFeature()
gapi.directory.resource.updateFeature()
else:
controlflow.invalid_argument_exit(argument, "gam update")
sys.exit(0)
@@ -12447,7 +12031,7 @@ def ProcessGAMCommand(args):
elif argument in ['org', 'ou']:
doGetOrgInfo()
elif argument == 'resource':
doGetResourceCalendarInfo()
gapi.directory.resource.getResourceCalendarInfo()
elif argument == 'cros':
doGetCrosInfo()
elif argument == 'mobile':
@@ -12479,7 +12063,7 @@ def ProcessGAMCommand(args):
elif argument in ['export', 'vaultexport']:
gapi.vault.getExportInfo()
elif argument in ['building']:
doGetBuildingInfo()
gapi.directory.resource.getBuildingInfo()
else:
controlflow.invalid_argument_exit(argument, "gam info")
sys.exit(0)
@@ -12501,7 +12085,7 @@ def ProcessGAMCommand(args):
elif argument == 'org':
doDeleteOrg()
elif argument == 'resource':
doDeleteResourceCalendar()
gapi.directory.resource.deleteResourceCalendar()
elif argument == 'mobile':
doDeleteMobile()
elif argument in ['schema', 'schemas']:
@@ -12529,9 +12113,9 @@ def ProcessGAMCommand(args):
elif argument in ['export', 'vaultexport']:
gapi.vault.deleteExport()
elif argument in ['building']:
doDeleteBuilding()
gapi.directory.resource.deleteBuilding()
elif argument in ['feature']:
doDeleteFeature()
gapi.directory.resource.deleteFeature()
elif argument in ['alert']:
doDeleteOrUndeleteAlert('delete')
elif argument in ['sakey', 'sakeys']:
@@ -12571,7 +12155,7 @@ def ProcessGAMCommand(args):
elif argument in ['orgs', 'ous']:
doPrintOrgs()
elif argument == 'resources':
doPrintResourceCalendars()
gapi.directory.resource.printResourceCalendars()
elif argument == 'cros':
doPrintCrosDevices()
elif argument == 'crosactivity':
@@ -12613,9 +12197,9 @@ def ProcessGAMCommand(args):
elif argument in ['exports', 'vaultexports']:
gapi.vault.printExports()
elif argument in ['building', 'buildings']:
doPrintBuildings()
gapi.directory.resource.printBuildings()
elif argument in ['feature', 'features']:
doPrintFeatures()
gapi.directory.resource.printFeatures()
elif argument in ['project', 'projects']:
doPrintShowProjects(True)
elif argument in ['alert', 'alerts']:

View File

@@ -0,0 +1,5 @@
import __main__
def buildGAPIObject():
return __main__.buildGAPIObject('directory')

View File

@@ -0,0 +1,487 @@
import sys
import uuid
import __main__
from var import *
import controlflow
import display
import gapi.directory
import utils
def printBuildings():
to_drive = False
cd = gapi.directory.buildGAPIObject()
titles = []
csvRows = []
fieldsList = ['buildingId']
# buildings.list() currently doesn't support paging
# but should soon, attempt to use it now so we
# won't break when it's turned on.
fields = 'nextPageToken,buildings(%s)'
possible_fields = {}
for pfield in cd._rootDesc['schemas']['Building']['properties']:
possible_fields[pfield.lower()] = pfield
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == 'todrive':
to_drive = True
i += 1
elif myarg == 'allfields':
fields = None
i += 1
elif myarg in possible_fields:
fieldsList.append(possible_fields[myarg])
i += 1
# Allows shorter arguments like "name" instead of "buildingname"
elif 'building'+myarg in possible_fields:
fieldsList.append(possible_fields['building'+myarg])
i += 1
else:
controlflow.invalid_argument_exit(
sys.argv[i], "gam print buildings")
if fields:
fields = fields % ','.join(fieldsList)
buildings = gapi.get_all_pages(cd.resources().buildings(), 'list',
'buildings',
customer=GC_Values[GC_CUSTOMER_ID],
fields=fields)
for building in buildings:
building.pop('etags', None)
building.pop('etag', None)
building.pop('kind', None)
if 'buildingId' in building:
building['buildingId'] = f'id:{building["buildingId"]}'
if 'floorNames' in building:
building['floorNames'] = ','.join(building['floorNames'])
building = utils.flatten_json(building)
for item in building:
if item not in titles:
titles.append(item)
csvRows.append(building)
display.sort_csv_titles('buildingId', titles)
display.write_csv_file(csvRows, titles, 'Buildings', to_drive)
def printResourceCalendars():
cd = gapi.directory.buildGAPIObject()
todrive = False
fieldsList = []
fieldsTitles = {}
titles = []
csvRows = []
query = None
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == 'todrive':
todrive = True
i += 1
elif myarg == 'query':
query = sys.argv[i+1]
i += 2
elif myarg == 'allfields':
fieldsList = []
fieldsTitles = {}
titles = []
for field in RESCAL_ALLFIELDS:
display.add_field_to_csv_file(field,
RESCAL_ARGUMENT_TO_PROPERTY_MAP,
fieldsList, fieldsTitles,
titles)
i += 1
elif myarg in RESCAL_ARGUMENT_TO_PROPERTY_MAP:
display.add_field_to_csv_file(myarg,
RESCAL_ARGUMENT_TO_PROPERTY_MAP,
fieldsList, fieldsTitles, titles)
i += 1
else:
controlflow.invalid_argument_exit(
sys.argv[i], "gam print resources")
if not fieldsList:
for field in RESCAL_DFLTFIELDS:
display.add_field_to_csv_file(field,
RESCAL_ARGUMENT_TO_PROPERTY_MAP,
fieldsList, fieldsTitles, titles)
fields = f'nextPageToken,items({",".join(set(fieldsList))})'
if 'buildingId' in fieldsList:
display.add_field_to_csv_file('buildingName', {'buildingName': [
'buildingName', ]}, fieldsList, fieldsTitles, titles)
__main__.printGettingAllItems('Resource Calendars', None)
page_message = gapi.got_total_items_first_last_msg('Resource Calendars')
resources = gapi.get_all_pages(cd.resources().calendars(), 'list',
'items', page_message=page_message,
message_attribute='resourceId',
customer=GC_Values[GC_CUSTOMER_ID],
query=query, fields=fields)
for resource in resources:
if 'featureInstances' in resource:
features = [a_feature['feature']['name'] for \
a_feature in resource['featureInstances']]
resource['featureInstances'] = ','.join(features)
if 'buildingId' in resource:
resource['buildingName'] = getBuildingNameById(
cd, resource['buildingId'])
resource['buildingId'] = f'id:{resource["buildingId"]}'
resUnit = {}
for field in fieldsList:
resUnit[fieldsTitles[field]] = resource.get(field, '')
csvRows.append(resUnit)
display.sort_csv_titles(
['resourceId', 'resourceName', 'resourceEmail'], titles)
display.write_csv_file(csvRows, titles, 'Resources', todrive)
RESCAL_DFLTFIELDS = ['id', 'name', 'email',]
RESCAL_ALLFIELDS = ['id', 'name', 'email', 'description', 'type',
'buildingid', 'category', 'capacity', 'features', 'floor',
'floorsection', 'generatedresourcename',
'uservisibledescription',]
RESCAL_ARGUMENT_TO_PROPERTY_MAP = {
'description': ['resourceDescription'],
'building': ['buildingId', ],
'buildingid': ['buildingId', ],
'capacity': ['capacity', ],
'category': ['resourceCategory', ],
'email': ['resourceEmail'],
'feature': ['featureInstances', ],
'features': ['featureInstances', ],
'floor': ['floorName', ],
'floorname': ['floorName', ],
'floorsection': ['floorSection', ],
'generatedresourcename': ['generatedResourceName', ],
'id': ['resourceId'],
'name': ['resourceName'],
'type': ['resourceType'],
'userdescription': ['userVisibleDescription', ],
'uservisibledescription': ['userVisibleDescription', ],
}
def printFeatures():
to_drive = False
cd = gapi.directory.buildGAPIObject()
titles = []
csvRows = []
fieldsList = ['name']
fields = 'nextPageToken,features(%s)'
possible_fields = {}
for pfield in cd._rootDesc['schemas']['Feature']['properties']:
possible_fields[pfield.lower()] = pfield
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == 'todrive':
to_drive = True
i += 1
elif myarg == 'allfields':
fields = None
i += 1
elif myarg in possible_fields:
fieldsList.append(possible_fields[myarg])
i += 1
elif 'feature'+myarg in possible_fields:
fieldsList.append(possible_fields['feature'+myarg])
i += 1
else:
controlflow.invalid_argument_exit(
sys.argv[i], "gam print features")
if fields:
fields = fields % ','.join(fieldsList)
features = gapi.get_all_pages(cd.resources().features(), 'list',
'features',
customer=GC_Values[GC_CUSTOMER_ID],
fields=fields)
for feature in features:
feature.pop('etags', None)
feature.pop('etag', None)
feature.pop('kind', None)
feature = utils.flatten_json(feature)
for item in feature:
if item not in titles:
titles.append(item)
csvRows.append(feature)
display.sort_csv_titles('name', titles)
display.write_csv_file(csvRows, titles, 'Features', to_drive)
def _getBuildingAttributes(args, body={}):
i = 0
while i < len(args):
myarg = args[i].lower().replace('_', '')
if myarg == 'id':
body['buildingId'] = args[i+1]
i += 2
elif myarg == 'name':
body['buildingName'] = args[i+1]
i += 2
elif myarg in ['lat', 'latitude']:
if 'coordinates' not in body:
body['coordinates'] = {}
body['coordinates']['latitude'] = args[i+1]
i += 2
elif myarg in ['long', 'lng', 'longitude']:
if 'coordinates' not in body:
body['coordinates'] = {}
body['coordinates']['longitude'] = args[i+1]
i += 2
elif myarg == 'description':
body['description'] = args[i+1]
i += 2
elif myarg == 'floors':
body['floorNames'] = args[i+1].split(',')
i += 2
else:
controlflow.invalid_argument_exit(
myarg, "gam create|update building")
return body
def createBuilding():
cd = gapi.directory.buildGAPIObject()
body = {'floorNames': ['1'],
'buildingId': str(uuid.uuid4()),
'buildingName': sys.argv[3]}
body = _getBuildingAttributes(sys.argv[4:], body)
print(f'Creating building {body["buildingId"]}...')
gapi.call(cd.resources().buildings(), 'insert',
customer=GC_Values[GC_CUSTOMER_ID], body=body)
def _makeBuildingIdNameMap(cd):
fields = 'nextPageToken,buildings(buildingId,buildingName)'
buildings = gapi.get_all_pages(cd.resources().buildings(), 'list',
'buildings',
customer=GC_Values[GC_CUSTOMER_ID],
fields=fields)
GM_Globals[GM_MAP_BUILDING_ID_TO_NAME] = {}
GM_Globals[GM_MAP_BUILDING_NAME_TO_ID] = {}
for building in buildings:
GM_Globals[GM_MAP_BUILDING_ID_TO_NAME][building['buildingId']
] = building['buildingName']
GM_Globals[GM_MAP_BUILDING_NAME_TO_ID][building['buildingName']
] = building['buildingId']
def getBuildingByNameOrId(cd, which_building, minLen=1):
if not which_building or \
(minLen == 0 and which_building in ['id:', 'uid:']):
if minLen == 0:
return ''
controlflow.system_error_exit(3, 'Building id/name is empty')
cg = UID_PATTERN.match(which_building)
if cg:
return cg.group(1)
if GM_Globals[GM_MAP_BUILDING_NAME_TO_ID] is None:
_makeBuildingIdNameMap(cd)
# Exact name match, return ID
if which_building in GM_Globals[GM_MAP_BUILDING_NAME_TO_ID]:
return GM_Globals[GM_MAP_BUILDING_NAME_TO_ID][which_building]
# No exact name match, check for case insensitive name matches
which_building_lower = which_building.lower()
ci_matches = []
for buildingName, buildingId in GM_Globals[GM_MAP_BUILDING_NAME_TO_ID].items():
if buildingName.lower() == which_building_lower:
ci_matches.append(
{'buildingName': buildingName, 'buildingId': buildingId})
# One match, return ID
if len(ci_matches) == 1:
return ci_matches[0]['buildingId']
# No or multiple name matches, try ID
# Exact ID match, return ID
if which_building in GM_Globals[GM_MAP_BUILDING_ID_TO_NAME]:
return which_building
# No exact ID match, check for case insensitive id match
for buildingId in GM_Globals[GM_MAP_BUILDING_ID_TO_NAME]:
# Match, return ID
if buildingId.lower() == which_building_lower:
return buildingId
# Multiple name matches
if len(ci_matches) > 1:
message = 'Multiple buildings with same name:\n'
for building in ci_matches:
message += f' Name:{building["buildingName"]} ' \
f'id:{building["buildingId"]}\n'
message += '\nPlease specify building name by exact case or by id.'
controlflow.system_error_exit(3, message)
# No matches
else:
controlflow.system_error_exit(3, f'No such building {which_building}')
def getBuildingNameById(cd, buildingId):
if GM_Globals[GM_MAP_BUILDING_ID_TO_NAME] is None:
_makeBuildingIdNameMap(cd)
return GM_Globals[GM_MAP_BUILDING_ID_TO_NAME].get(buildingId, 'UNKNOWN')
def updateBuilding():
cd = gapi.directory.buildGAPIObject()
buildingId = getBuildingByNameOrId(cd, sys.argv[3])
body = _getBuildingAttributes(sys.argv[4:])
print(f'Updating building {buildingId}...')
gapi.call(cd.resources().buildings(), 'patch',
customer=GC_Values[GC_CUSTOMER_ID], buildingId=buildingId,
body=body)
def getBuildingInfo():
cd = gapi.directory.buildGAPIObject()
buildingId = getBuildingByNameOrId(cd, sys.argv[3])
building = gapi.call(cd.resources().buildings(), 'get',
customer=GC_Values[GC_CUSTOMER_ID],
buildingId=buildingId)
if 'buildingId' in building:
building['buildingId'] = f'id:{building["buildingId"]}'
if 'floorNames' in building:
building['floorNames'] = ','.join(building['floorNames'])
if 'buildingName' in building:
sys.stdout.write(building.pop('buildingName'))
display.print_json(building)
def deleteBuilding():
cd = gapi.directory.buildGAPIObject()
buildingId = getBuildingByNameOrId(cd, sys.argv[3])
print(f'Deleting building {buildingId}...')
gapi.call(cd.resources().buildings(), 'delete',
customer=GC_Values[GC_CUSTOMER_ID], buildingId=buildingId)
def _getFeatureAttributes(args, body={}):
i = 0
while i < len(args):
myarg = args[i].lower().replace('_', '')
if myarg == 'name':
body['name'] = args[i+1]
i += 2
else:
controlflow.invalid_argument_exit(
myarg, "gam create|update feature")
return body
def createFeature():
cd = gapi.directory.buildGAPIObject()
body = _getFeatureAttributes(sys.argv[3:])
print(f'Creating feature {body["name"]}...')
gapi.call(cd.resources().features(), 'insert',
customer=GC_Values[GC_CUSTOMER_ID], body=body)
def updateFeature():
# update does not work for name and name is only field to be updated
# if additional writable fields are added to feature in the future
# we'll add support for update as well as rename
cd = gapi.directory.buildGAPIObject()
oldName = sys.argv[3]
body = {'newName': sys.argv[5:]}
print(f'Updating feature {oldName}...')
gapi.call(cd.resources().features(), 'rename',
customer=GC_Values[GC_CUSTOMER_ID], oldName=oldName,
body=body)
def deleteFeature():
cd = gapi.directory.buildGAPIObject()
featureKey = sys.argv[3]
print(f'Deleting feature {featureKey}...')
gapi.call(cd.resources().features(), 'delete',
customer=GC_Values[GC_CUSTOMER_ID], featureKey=featureKey)
def _getResourceCalendarAttributes(cd, args, body={}):
i = 0
while i < len(args):
myarg = args[i].lower().replace('_', '')
if myarg == 'name':
body['resourceName'] = args[i+1]
i += 2
elif myarg == 'description':
body['resourceDescription'] = args[i+1].replace('\\n', '\n')
i += 2
elif myarg == 'type':
body['resourceType'] = args[i+1]
i += 2
elif myarg in ['building', 'buildingid']:
body['buildingId'] = getBuildingByNameOrId(
cd, args[i+1], minLen=0)
i += 2
elif myarg in ['capacity']:
body['capacity'] = __main__.getInteger(args[i+1], myarg, minVal=0)
i += 2
elif myarg in ['feature', 'features']:
features = args[i+1].split(',')
body['featureInstances'] = []
for feature in features:
instance = {'feature': {'name': feature}}
body['featureInstances'].append(instance)
i += 2
elif myarg in ['floor', 'floorname']:
body['floorName'] = args[i+1]
i += 2
elif myarg in ['floorsection']:
body['floorSection'] = args[i+1]
i += 2
elif myarg in ['category']:
body['resourceCategory'] = args[i+1].upper()
if body['resourceCategory'] == 'ROOM':
body['resourceCategory'] = 'CONFERENCE_ROOM'
i += 2
elif myarg in ['uservisibledescription', 'userdescription']:
body['userVisibleDescription'] = args[i+1]
i += 2
else:
controlflow.invalid_argument_exit(
args[i], "gam create|update resource")
return body
def createResourceCalendar():
cd = gapi.directory.buildGAPIObject()
body = {'resourceId': sys.argv[3],
'resourceName': sys.argv[4]}
body = _getResourceCalendarAttributes(cd, sys.argv[5:], body)
print(f'Creating resource {body["resourceId"]}...')
gapi.call(cd.resources().calendars(), 'insert',
customer=GC_Values[GC_CUSTOMER_ID], body=body)
def updateResourceCalendar():
cd = gapi.directory.buildGAPIObject()
resId = sys.argv[3]
body = _getResourceCalendarAttributes(cd, sys.argv[4:])
# Use patch since it seems to work better.
# update requires name to be set.
gapi.call(cd.resources().calendars(), 'patch',
customer=GC_Values[GC_CUSTOMER_ID], calendarResourceId=resId,
body=body, fields='')
print(f'updated resource {resId}')
def getResourceCalendarInfo():
cd = gapi.directory.buildGAPIObject()
resId = sys.argv[3]
resource = gapi.call(cd.resources().calendars(), 'get',
customer=GC_Values[GC_CUSTOMER_ID],
calendarResourceId=resId)
if 'featureInstances' in resource:
features = []
for a_feature in resource.pop('featureInstances'):
features.append(a_feature['feature']['name'])
resources['features'] = ', '.join(features)
if 'buildingId' in resource:
resource['buildingName'] = getBuildingNameById(
cd, resource['buildingId'])
resource['buildingId'] = f'id:{resource["buildingId"]}'
display.print_json(resource)
def doDeleteResourceCalendar():
resId = sys.argv[3]
cd = gapi.directory.buildGAPIObject()
print(f'Deleting resource calendar {resId}')
gapi.call(cd.resources().calendars(), 'delete',
customer=GC_Values[GC_CUSTOMER_ID], calendarResourceId=resId)

View File

@@ -384,7 +384,7 @@ def getHoldInfo():
def convertExportNameToID(v, nameOrID, matterId):
nameOrID = nameOrID.lower()
cg = __main__.UID_PATTERN.match(nameOrID)
cg = UID_PATTERN.match(nameOrID)
if cg:
return cg.group(1)
fields = 'exports(id,name),nextPageToken'
@@ -399,7 +399,7 @@ def convertExportNameToID(v, nameOrID, matterId):
def convertHoldNameToID(v, nameOrID, matterId):
nameOrID = nameOrID.lower()
cg = __main__.UID_PATTERN.match(nameOrID)
cg = UID_PATTERN.match(nameOrID)
if cg:
return cg.group(1)
fields = 'holds(holdId,name),nextPageToken'
@@ -414,7 +414,7 @@ def convertHoldNameToID(v, nameOrID, matterId):
def convertMatterNameToID(v, nameOrID):
nameOrID = nameOrID.lower()
cg = __main__.UID_PATTERN.match(nameOrID)
cg = UID_PATTERN.match(nameOrID)
if cg:
return cg.group(1)
fields = 'matters(matterId,name),nextPageToken'

View File

@@ -6,7 +6,7 @@ import platform
import re
gam_author = 'Jay Lee <jay0lee@gmail.com>'
gam_version = '4.99'
gam_version = '5.00'
gam_license = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
GAM_URL = 'https://git.io/gam'
@@ -1197,3 +1197,5 @@ YYYYMMDD_FORMAT_REQUIRED = 'yyyy-mm-dd'
YYYYMMDDTHHMMSS_FORMAT_REQUIRED = 'yyyy-mm-ddThh:mm:ss[.fff](Z|(+|-(hh:mm)))'
YYYYMMDD_PATTERN = re.compile(r'^[0-9]{4}-[0-9]{2}-[0-9]{2}$')
UID_PATTERN = re.compile(r'u?id: ?(.+)', re.IGNORECASE)