Skip to content

Commit

Permalink
Added option splitupdate to gam <UserTypeEntity> update calattendees
Browse files Browse the repository at this point in the history
  • Loading branch information
taers232c committed Mar 30, 2021
1 parent 7e4cca3 commit 28c1f7c
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 43 deletions.
20 changes: 13 additions & 7 deletions src/GamCommands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3661,7 +3661,7 @@ gam info user [<UserItem>]
[nobuildingnames|buildingnames]
[nogroups|groups]
[nolicenses|nolicences|licenses|licences]
[noschemas|allschemas|(schemas|custom <SchemaNameList>)|(customschemas <SchemaNameList>)]
[noschemas|allschemas|(schemas|custom|customschemas <SchemaNameList>)]
[userview] <UserFieldName>* [fields <UserFieldNameList>]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)] [formatjson]

Expand All @@ -3686,7 +3686,7 @@ gam info users <UserTypeEntity>
[nobuildingnames|buildingnames]
[nogroups|groups]
[nolicenses|nolicences|licenses|licences]
[noschemas|allschemas|(schemas|custom <SchemaNameList>)|(customschemas <SchemaNameList>)]
[noschemas|allschemas|(schemas|custom|customschemas <SchemaNameList>)]
[userview] <UserFieldName>* [fields <UserFieldNameList>]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)] [formatjson]

Expand All @@ -3711,7 +3711,7 @@ gam <UserTypeEntity> info users
[nobuildingnames|buildingnames]
[nogroups|groups]
[nolicenses|nolicences|licenses|licences]
[noschemas|allschemas|(schemas|custom <SchemaNameList>)|(customschemas <SchemaNameList>)]
[noschemas|allschemas|(schemas|custom|customschemas <SchemaNameList>)]
[userview] <UserFieldName>* [fields <UserFieldNameList>]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)] [formatjson]

Expand All @@ -3724,7 +3724,9 @@ gam print users [todrive <ToDriveAttribute>*]
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitPath>] [deleted_only|only_deleted])
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns] [license|licenses|licence|licences] [emailpart|emailparts|username] [schemas|custom all|<SchemaNameList>]
[groups|groupsincolumns] [license|licenses|licence|licences]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
[userview] [basic|full|allfields | <UserFieldName>* | fields <UserFieldNameList>]
[delimiter <Character>] [sortheaders [<Boolean>]] [scalarsfirst [<Boolean>]]
[formatjson [quotechar <Character>]] [quoteplusphonenumbers]
Expand All @@ -3734,15 +3736,19 @@ Print fields for specified users.

gam print users [todrive <ToDriveAttribute>*] select <UserTypeEntity>
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns] [license|licenses|licence|licences] [emailpart|emailparts|username] [schemas|custom all|<SchemaNameList>]
[groups|groupsincolumns] [license|licenses|licence|licences]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
[userview] [basic|full|allfields | <UserFieldName>* | fields <UserFieldNameList>]
[delimiter <Character>] [sortheaders [<Boolean>]] [scalarsfirst [<Boolean>]]
[formatjson [quotechar <Character>]] [quoteplusphonenumbers]
[issuspended <Boolean>]

gam <UserTypeEntity> print users [todrive <ToDriveAttribute>*]
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns] [license|licenses|licence|licences] [emailpart|emailparts|username] [schemas|custom all|<SchemaNameList>]
[groups|groupsincolumns] [license|licenses|licence|licences]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
[userview] [basic|full|allfields | <UserFieldName>* | fields <UserFieldNameList>]
[delimiter <Character>] [sortheaders [<Boolean>]] [scalarsfirst [<Boolean>]]
[formatjson [quotechar <Character>]] [quoteplusphonenumbers]
Expand Down Expand Up @@ -4027,7 +4033,7 @@ gam <UserTypeEntity> print events <UserCalendarEntity> [<EventSelectEntity>] <Ev
[countsonly] [formatjson [quotechar <Character>]] [todrive <ToDriveAttribute>*]

gam <UserTypeEntity> update calattendees <UserCalendarEntity> <EventEntity> [anyorganizer]
[<EventNotificationAttribute>] [doit]
[<EventNotificationAttribute>] [splitupdate] [doit]
(csv <FileName>|(gsheet <UserGoogleSheet>))*
(delete <EmailAddress>)*
(deleteentity <EmailAddressEntity>)*
Expand Down
13 changes: 11 additions & 2 deletions src/GamUpdate.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
6.00.07

Added option `splitupdate` to `gam <UserTypeEntity> update calattendees` to handle replacing
an email alias with its primary email in the event attendee list.
By default, when you try to replace an email alias with its primary email, the Google Calendar API
detects that the underlying user ID is the same and doesn't change the address. The `splitupdate`
option causes GAM to make two updates to the attendee list; the first removes the alias and
the second adds the primary email.

6.00.06

Added command `gam print addresses [todrive <ToDriveAttribute>*]` that produces a
Expand All @@ -9,7 +18,7 @@ SuspendedUser, User, UserAlias, UserNEAlias. 'NE' is and abbreviation for NonEdi

Fixed bug in `gam print vaultcounts ... everyone` which caused the following error:
```
ERROR: getUsersToModify coding error
ERROR: getUsersToModify coding error
```

6.00.04
Expand All @@ -20,7 +29,7 @@ Previously, you had to specify each email notification event type individually (
```
<CalendarEmailNotificatonEventType> ::=
eventcreation|eventchange|eventcancellation|eventresponse|agenda
<CalendarEmailNotificatonEventTypeList> ::=
<CalendarEmailNotificatonEventTypeList> ::=
<CalendarEmailNotificatonEventType>(,<CalendarEmailNotificatonEventType>)*

<CalendarAttribute> ::=
Expand Down
98 changes: 64 additions & 34 deletions src/gam/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"""

__author__ = 'Ross Scroggs <[email protected]>'
__version__ = '6.00.06'
__version__ = '6.00.07'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'

import base64
Expand Down Expand Up @@ -26221,14 +26221,13 @@ def _validateCalendarGetEvents(origUser, user, origCal, calId, j, jcount, calend
except (GAPI.notFound, GAPI.deleted) as e:
if not checkCalendarExists(cal, calId):
entityUnknownWarning(Ent.CALENDAR, calId, j, jcount)
return (calId, cal, [], 0)
entityActionFailedWarning([Ent.CALENDAR, calId, Ent.EVENT, eventId], str(e), k, kcount)
else:
entityActionFailedWarning([Ent.CALENDAR, calId, Ent.EVENT, eventId], str(e), j, jcount)
except (GAPI.notACalendarUser, GAPI.forbidden, GAPI.invalid) as e:
entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount)
return (calId, cal, [], 0)
except (GAPI.serviceNotAvailable, GAPI.authError):
entityServiceNotApplicableWarning(Ent.CALENDAR, calId, j, jcount)
return (calId, cal, [], 0)
return (calId, cal, [], 0)

def _getCalendarCreateImportUpdateEventOptions(function, calendarEventEntity=None):
body = {}
Expand Down Expand Up @@ -30919,9 +30918,8 @@ def _callbackGetLicense(request_id, response, exception):
elif myarg in {'custom', 'schemas', 'customschemas'}:
getSchemas = True
projection = 'custom'
customFieldMask = getString(Cmd.OB_SCHEMA_NAME_LIST)
if myarg == 'customschemas':
fieldsList.append('customSchemas')
customFieldMask = getString(Cmd.OB_SCHEMA_NAME_LIST).replace(' ', ',')
fieldsList.append('customSchemas')
elif myarg in {'products', 'product'}:
skus = SKU.convertProductListToSKUList(getGoogleProductList())
elif myarg in {'sku', 'skus'}:
Expand All @@ -30938,6 +30936,8 @@ def _callbackGetLicense(request_id, response, exception):
FJQC.GetFormatJSON(myarg)
if fieldsList:
fieldsList.append('primaryEmail')
if getAliases:
fieldsList.append('aliases')
fields = getFieldsFromFieldsList(fieldsList)
if getLicenses:
lic = buildGAPIObject(API.LICENSING)
Expand Down Expand Up @@ -31230,7 +31230,7 @@ def _callbackGetLicense(request_id, response, exception):
# [nobuildingnames|buildingnames]
# [nogroups|groups]
# [nolicenses|nolicences|licenses|licences]
# [noschemas|allschemas|(schemas|custom <SchemaNameList>)|(customschemas <SchemaNameList>)]
# [noschemas|allschemas|(schemas|custom|customschemas <SchemaNameList>)]
# [userview] <UserFieldName>* [fields <UserFieldNameList>]
# [(products|product <ProductIDList>)|(skus|sku <SKUIDList>)] [formatjson]
def doInfoUsers():
Expand All @@ -31242,7 +31242,7 @@ def doInfoUsers():
# [nobuildingnames|buildingnames]
# [nogroups|groups]
# [nolicenses|nolicences|licenses|licences]
# [noschemas|allschemas|(schemas|custom <SchemaNameList>)|(customschemas <SchemaNameList>)]
# [noschemas|allschemas|(schemas|custom|customschemas <SchemaNameList>)]
# [userview] <UserFieldName>* [fields <UserFieldNameList>]
# [(products|product <ProductIDList>)|(skus|sku <SKUIDList>)] [formatjson]
# gam info user
Expand Down Expand Up @@ -36528,7 +36528,7 @@ def emptyCalendarTrash(users):
Ind.Decrement()

# gam <UserTypeEntity> update calattendees <UserCalendarEntity> <EventEntity> [anyorganizer]
# [<EventNotificationAttribute>] [doit]
# [<EventNotificationAttribute>] [splitupdate] [doit]
# (csv <FileName>|(gsheet <UserGoogleSheet>))*
# (delete <EmailAddress>)*
# (deleteentity <EmailAddressEntity>)*
Expand All @@ -36549,7 +36549,7 @@ def getStatus(option):

calendarEntity = getUserCalendarEntity()
calendarEventEntity = getCalendarEventEntity()
anyOrganizer = doIt = False
anyOrganizer = doIt = splitUpdate = False
parameters = {'sendUpdates': 'none'}
attendeeMap = {}
errors = 0
Expand Down Expand Up @@ -36621,13 +36621,17 @@ def getStatus(option):
pass
elif myarg == 'doit':
doIt = True
elif myarg == 'splitupdate':
splitUpdate = True
else:
unknownArgumentExit()
if not attendeeMap:
missingArgumentExit(Msg.UPDATE_ATTENDEE_CHANGES)
ucount = len(attendeeMap)
if errors:
systemErrorExit(USAGE_ERROR_RC, '')
removeMessage = Msg.ATTENDEES_REMOVE
addMessage = Msg.ATTENDEES_ADD_REMOVE if not splitUpdate else Msg.ATTENDEES_ADD
fieldsList = ['attendees', 'id', 'organizer', 'status', 'summary']
i, count, users = getEntityArgument(users)
for user in users:
Expand Down Expand Up @@ -36659,18 +36663,23 @@ def getStatus(option):
needsUpdate = False
for _, v in sorted(iter(attendeeMap.items())):
v['done'] = False
updatedAttendees = []
updatedAttendeesAdd = []
updatedAttendeesRemove = []
entityPerformActionNumItems([Ent.EVENT, eventSummary], ucount, Ent.ATTENDEE, k, kcount)
Ind.Increment()
u = 0
for attendee in event.get('attendees', []):
oldAddr = attendee.get('email', '').lower()
if not oldAddr:
updatedAttendees.append(attendee)
updatedAttendeesAdd.append(attendee)
if splitUpdate:
updatedAttendeesRemove.append(attendee)
continue
update = attendeeMap.get(oldAddr)
if not update:
updatedAttendees.append(attendee)
updatedAttendeesAdd.append(attendee)
if splitUpdate:
updatedAttendeesRemove.append(attendee)
continue
updOp = update['op']
if updOp == 'delete':
Expand All @@ -36697,7 +36706,7 @@ def getStatus(option):
else:
Act.Set(Act.SKIP)
entityPerformAction([Ent.EVENT, eventSummary, Ent.ATTENDEE, oldAddr], u, ucount)
updatedAttendees.append(attendee)
updatedAttendeesAdd.append(attendee)
else: #replace
u += 1
update['done'] = True
Expand All @@ -36706,7 +36715,7 @@ def getStatus(option):
attendee['optional'] = updOptional if updOptional is not None else oldOptional
Act.Set(Act.REPLACE)
entityPerformActionModifierNewValue([Ent.EVENT, eventSummary, Ent.ATTENDEE, oldAddr], Act.MODIFIER_WITH, update['email'], u, ucount)
updatedAttendees.append(attendee)
updatedAttendeesAdd.append(attendee)
needsUpdate = True
for newAddr, v in sorted(iter(attendeeMap.items())):
if v['op'] == 'add' and not v['done']:
Expand All @@ -36719,7 +36728,7 @@ def getStatus(option):
attendee['optional'] = v['optional']
Act.Set(Act.ADD)
entityPerformAction([Ent.EVENT, eventSummary, Ent.ATTENDEE, newAddr], u, ucount)
updatedAttendees.append(attendee)
updatedAttendeesAdd.append(attendee)
needsUpdate = True
for newAddr, v in sorted(iter(attendeeMap.items())):
if not v['done']:
Expand All @@ -36730,23 +36739,44 @@ def getStatus(option):
if needsUpdate:
Act.Set(Act.UPDATE)
if doIt:
try:
callGAPI(cal.events(), 'patch',
throwReasons=GAPI.CALENDAR_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID],
calendarId=calId, eventId=event['id'], body={'attendees': updatedAttendees},
sendUpdates=parameters['sendUpdates'], fields='')
entityActionPerformed([Ent.EVENT, eventSummary], j, jcount)
except GAPI.notFound as e:
if not checkCalendarExists(cal, calId):
entityUnknownWarning(Ent.CALENDAR, calId, j, jcount)
status = True
if splitUpdate:
try:
callGAPI(cal.events(), 'patch',
throwReasons=GAPI.CALENDAR_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID],
calendarId=calId, eventId=event['id'], body={'attendees': updatedAttendeesRemove},
sendUpdates=parameters['sendUpdates'], fields='')
entityActionPerformedMessage([Ent.EVENT, eventSummary], removeMessage, j, jcount)
except GAPI.notFound as e:
if not checkCalendarExists(cal, calId):
entityUnknownWarning(Ent.CALENDAR, calId, j, jcount)
break
entityActionFailedWarning([Ent.CALENDAR, calId, Ent.EVENT, eventSummary], str(e), k, kcount)
status = False
except (GAPI.notACalendarUser, GAPI.forbidden, GAPI.invalid) as e:
entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount)
break
except (GAPI.serviceNotAvailable, GAPI.authError):
entityServiceNotApplicableWarning(Ent.CALENDAR, calId, j, jcount)
break
if status:
try:
callGAPI(cal.events(), 'patch',
throwReasons=GAPI.CALENDAR_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID],
calendarId=calId, eventId=event['id'], body={'attendees': updatedAttendeesAdd},
sendUpdates=parameters['sendUpdates'], fields='')
entityActionPerformedMessage([Ent.EVENT, eventSummary], addMessage, jcount)
except GAPI.notFound as e:
if not checkCalendarExists(cal, calId):
entityUnknownWarning(Ent.CALENDAR, calId, j, jcount)
break
entityActionFailedWarning([Ent.CALENDAR, calId, Ent.EVENT, eventSummary], str(e), k, kcount)
except (GAPI.notACalendarUser, GAPI.forbidden, GAPI.invalid) as e:
entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount)
break
except (GAPI.serviceNotAvailable, GAPI.authError):
entityServiceNotApplicableWarning(Ent.CALENDAR, calId, j, jcount)
break
entityActionFailedWarning([Ent.CALENDAR, calId, Ent.EVENT, eventSummary], str(e), k, kcount)
except (GAPI.notACalendarUser, GAPI.forbidden, GAPI.invalid) as e:
entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount)
break
except (GAPI.serviceNotAvailable, GAPI.authError):
entityServiceNotApplicableWarning(Ent.CALENDAR, calId, j, jcount)
break
else:
entityActionNotPerformedWarning([Ent.EVENT, eventSummary], Msg.USE_DOIT_ARGUMENT_TO_PERFORM_ACTION, j, jcount)
Ind.Decrement()
Expand Down
3 changes: 3 additions & 0 deletions src/gam/gamlib/glmsgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@
API_ERROR_SETTINGS = 'API error, some settings not set'
ARE_MUTUALLY_EXCLUSIVE = 'Arguments {0} and {1} are mutually exclusive'
AS = 'as'
ATTENDEES_ADD = 'Add Attendees'
ATTENDEES_ADD_REMOVE = 'Add/Remove Attendees'
ATTENDEES_REMOVE = 'Remove Attendees'
AUTHORIZATION_FILE_ALREADY_EXISTS = '{0} already exists. Please delete or rename it before attempting to {1} project.'
AUTHENTICATION_FLOW_COMPLETE = 'The authentication flow has completed. You may close this browser window and return to GAM.'
AUTHENTICATION_FLOW_FAILED = 'The authentication flow failed (invalid verification code entered?): {0}'
Expand Down

0 comments on commit 28c1f7c

Please sign in to comment.