From b41a6b1d60f47a53a2165c687205b1f2e38166fa Mon Sep 17 00:00:00 2001 From: Jay Lee Date: Mon, 9 Mar 2020 20:15:19 -0400 Subject: [PATCH] resources, buildings and features to resource.py --- src/gam.py | 450 +--------------- src/gapi/directory/__init__.py | 5 + src/gapi/directory/resource.py | 487 ++++++++++++++++++ src/gapi/vault.py | 6 +- .../.linux-x86_64-before-install.sh.swp | Bin 20480 -> 0 bytes src/var.py | 4 +- 6 files changed, 515 insertions(+), 437 deletions(-) create mode 100644 src/gapi/directory/__init__.py create mode 100644 src/gapi/directory/resource.py delete mode 100644 src/travis/.linux-x86_64-before-install.sh.swp diff --git a/src/gam.py b/src/gam.py index b98249b0..775f3504 100755 --- a/src/gam.py +++ b/src/gam.py @@ -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']: diff --git a/src/gapi/directory/__init__.py b/src/gapi/directory/__init__.py new file mode 100644 index 00000000..68e2d0cf --- /dev/null +++ b/src/gapi/directory/__init__.py @@ -0,0 +1,5 @@ +import __main__ + + +def buildGAPIObject(): + return __main__.buildGAPIObject('directory') diff --git a/src/gapi/directory/resource.py b/src/gapi/directory/resource.py new file mode 100644 index 00000000..c48322f2 --- /dev/null +++ b/src/gapi/directory/resource.py @@ -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) diff --git a/src/gapi/vault.py b/src/gapi/vault.py index b8598d26..f7004610 100644 --- a/src/gapi/vault.py +++ b/src/gapi/vault.py @@ -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' diff --git a/src/travis/.linux-x86_64-before-install.sh.swp b/src/travis/.linux-x86_64-before-install.sh.swp deleted file mode 100644 index 7aa67d0d48de138ca69953a15d574126dbb746e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 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 A%m4rY diff --git a/src/var.py b/src/var.py index 162cb983..c1363de7 100644 --- a/src/var.py +++ b/src/var.py @@ -6,7 +6,7 @@ import platform import re gam_author = 'Jay Lee ' -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) \ No newline at end of file