Two updates

Fixed bug in `gam <UserTypeEntity> create contact <JSONData>` that caused a trap when
contacts were being copied from one user to another.

Updated the commands to allow specification of a task list by its title.
This commit is contained in:
Ross Scroggs
2023-10-19 08:14:03 -07:00
parent 7e9207ae3c
commit 55298f0134
4 changed files with 154 additions and 80 deletions

View File

@@ -1,24 +1,18 @@
- [Introduction](#introduction)
- [Requirements](#requirements)
- [Installation - First time GAM installation](#installation---first-time-GAM-installation)
- [Installation - Upgrading from a GAM version other than a prior version of GAMADV-X or GAMADV-XTD or GAMADV-XTD3](#installation---upgrading-from-a-gam-version-other-than-a-prior-version-of-gamadv-x-or-gamadv-xtd-or-gamadv-xtd3)
- [Installation - Upgrading from a prior version of GAMADV-X or GAMADV-XTD or GAMADV-XTD3](#installation---upgrading-from-a-prior-version-of-gamadv-x-or-gamadv-xtd-or-gamadv-xtd3)
# Introduction # Introduction
GAMADV-XTD3 is a free, open source command line tool for Google Workspace Administrators to manage domain and user settings quickly and easily. GAMADV-XTD3 is a free, open source command line tool for Google Workspace (formerly G Suite) Administrators to manage domain and user settings quickly and easily.
GAMADV-XTD3 is built with Python 3; as Python 2 support ends on 2020-01-01, this is the version of Advanced GAM that new/existing users should install. GAMADV-XTD3 is built with Python 3.
This page provides simple instructions for downloading, installing and starting to use GAMADV-XTD3. This page provides simple instructions for downloading, installing and starting to use GAMADV-XTD3.
GAMADV-XTD3 requires paid, or Education/Non-profit, editions of Google Workspace. G Suite Legacy Free Edition has limited API support and not all GAM commands work. GAMADV-XTD3 runs on all versions of Google Workspace; Google Apps Free Edition has limited API support and not all GAM commands work.
GAMADV-XTD3 is a rewrite/extension of Jay Lee's [GAM], without his efforts, this version wouldn't exist. GAMADV-XTD3 is a rewrite/extension of Jay Lee's [GAM], without his efforts, this version wouldn't exist.
GAMADV-XTD3 is backwards compatible with [GAM], meaning that if your command works with regular GAM, it will also work with GAMADV-XTD3. There may be differences in output, but the syntax is compatible. GAMADV-XTD3 is backwards compatible with [GAM], meaning that if your command works with regular GAM, it will also work with GAMADV-XTD3. There may be differences in output, but the syntax is compatible.
# Documentation # Documentation
Basic GAM documentation is hosted in the [GitHub Wiki]. Documentation specifically for GAMADV-XTD3 is hosted in the [GitHub GAMADV-XTD3 Wiki] and in Gam*.txt files. Documentation for GAMADV-XTD3 is hosted in the [GitHub GAMADV-XTD3 Wiki] and in Gam*.txt files.
# Mailing List / Discussion group # Mailing List / Discussion group
The GAM mailing list / discussion group is hosted on [Google Groups]. You can join the list and interact via email, or just post from the web itself. The GAM mailing list / discussion group is hosted on [Google Groups]. You can join the list and interact via email, or just post from the web itself.
@@ -32,39 +26,48 @@ GAMADV-XTD3 is maintained by <a href="mailto:ross.scroggs@gmail.com">Ross Scrogg
# Requirements # Requirements
To run all commands properly, GAMADV-XTD3 requires three things: To run all commands properly, GAMADV-XTD3 requires three things:
* An API project which identifies your install of GAMADV-XTD3 to Google and keeps track of API quotas. * An API project which identifies your install of GAMADV-XTD3 to Google and keeps track of API quotas.
* Authorization to act as your Google Workspace Administrator in order to perform management functions like add users, modify group settings and membership and pull domain reports. * Authorization to act as your G Suite Administrator in order to perform management functions like add users, modify group settings and membership and pull domain reports.
* A special service account that is authorized to act on behalf of your users in order to modify user-specific settings and data such as Drive files, Calendars and Gmail messages and settings like signatures. * A special service account that is authorized to act on behalf of your users in order to modify user-specific settings and data such as Drive files, Calendars and Gmail messages and settings like signatures.
# Installation - First time GAM installation # Installation - First time GAM installation
Use these steps if you have never used any version of GAM in your domain. They will create a GAM project Use these steps if you have never used any version of GAM in your domain. They will create a GAM project
and all necessary authentications. and all necessary authentications.
* Download: [Downloads](Downloads) | [Downloads] | [Configuration] | [Install] |
* Configuration: [GAM Configuration](gam.cfg) | :---: | :---: | :---: |
* Install: [How to Install Advanced GAM](How-to-Install-Advanced-GAM)
# Installation - Upgrading from a GAM version other than a prior version of GAMADV-X or GAMADV-XTD or GAMADV-XTD3 # Installation - Update Advanced GAM
Use these steps if you have used any version of GAM in your domain. They will update your GAM project Use these steps to update your version of GAMADV-XTD3.
| [Downloads] | [Configuration] | [UpdateAdvanced] |
| :---: | :---: | :---: |
# Installation - Upgrading from Standard GAM
Use these steps if you have used any version of Standard GAM in your domain. They will update your GAM project
and all necessary authentications. and all necessary authentications.
* Download: [Downloads](Downloads) | [Downloads] | [Configuration] | [UpgradeFromStandard] |
* Configuration: [GAM Configuration](gam.cfg) | :---: | :---: | :---: |
* Upgrade: [How to Upgrade from Standard GAM](How-to-Upgrade-from-Standard-GAM)
# Installation - Upgrading from a prior version of GAMADV-X or GAMADV-XTD or GAMADV-XTD3 # Installation - Upgrading from a prior version of GAMADV-X or GAMADV-XTD
Use these steps if you already use GAMADV-X or GAMADV-XTD or GAMADV-XTD3. The updates may tell you to update your GAM project Use these steps if you already use GAMADV-X or GAMADV-XTD. The updates may tell you to update your GAM project
or authentications because new features have been included. or authentications because new features have been included.
* Updates: [GAM Updates] | [Updates] | [Downloads] | [UpgradeFromAdvanced] |
* Download: [Downloads](Downloads) | :---: | :---: | :---: |
# Multiple Versions
You can install multiple versions of GAM and GAMADV-XTD3 in different parallel directories. You can install multiple versions of GAM and GAMADV-XTD3 in different parallel directories.
[GAM]: https://github.com/GAM-team/GAM [GAM]: https://github.com/GAM-team/GAM
[GitHub Releases]: https://github.com/taers232c/GAMADV-XTD3/releases [GitHub Releases]: https://github.com/taers232c/GAMADV-XTD3/releases
[GitHub]: https://github.com/taers232c/GAMADV-XTD3/tree/master [GitHub]: https://github.com/taers232c/GAMADV-XTD3/tree/master
[GitHub Wiki]: https://github.com/GAM-team/GAM/wiki/ [GitHub GAMADV-XTD3 Wiki]: https://github.com/taers232c/GAMADV-XTD3/wiki
[GitHub GAMADV-XTD3 Wiki]: https://github.com/taers232c/GAMADV-XTD3/wiki/
[Google Groups]: https://groups.google.com/group/google-apps-manager [Google Groups]: https://groups.google.com/group/google-apps-manager
[GAM Updates]: https://github.com/taers232c/GAMADV-XTD3/wiki/GamUpdates [Downloads]: https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads
[Configuration]: https://github.com/taers232c/GAMADV-XTD3/wiki/gam.cfg
[Install]: https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Install-Advanced-GAM
[UpdateAdvanced]: https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Update-Advanced-GAM
[UpgradeFromStandard]: https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Upgrade-from-Standard-GAM
[Updates]: https://github.com/taers232c/GAMADV-XTD3/wiki/GAM-Updates
[UpgradeFromAdvanced]: https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Upgrade-from-GAMADV-X-or-GAMADV-XTD

View File

@@ -588,6 +588,7 @@ If an item contains spaces, it should be surrounded by ".
<TakeoutBucketName> ::= takeout-export-[a-f,0-9,-]* <TakeoutBucketName> ::= takeout-export-[a-f,0-9,-]*
<TaskID> ::= <String> <TaskID> ::= <String>
<TaskListID> ::= <String> <TaskListID> ::= <String>
<TaskListTitle> ::= tltitle:<String>
<TasklistIDTaskID> ::= <TasklistID>/<TaskID> <TasklistIDTaskID> ::= <TasklistID>/<TaskID>
<ThreadID> ::= <String> <ThreadID> ::= <String>
<TimeZone> ::= <String> <TimeZone> ::= <String>
@@ -720,6 +721,7 @@ If an item contains spaces, it should be surrounded by ".
<SharedDriveIDList> ::= "<SharedDriveID>(,<SharedDriveID>)*" <SharedDriveIDList> ::= "<SharedDriveID>(,<SharedDriveID>)*"
<StringList> ::= "<String>(,<String>)*" <StringList> ::= "<String>(,<String>)*"
<TasklistIDList> ::= "<TasklistID>(,<TasklistID>)*" <TasklistIDList> ::= "<TasklistID>(,<TasklistID>)*"
<TasklistTitleList> ::= "'<TasklistTitle>'(,'<TasklistTitle>')*"
<TasklistIDTaskIDList> ::= "<TasklistIDTaskID>(,<TasklistIDTaskID>)*" <TasklistIDTaskIDList> ::= "<TasklistIDTaskID>(,<TasklistIDTaskID>)*"
<ThreadIDList> ::= "<ThreadID>(,<ThreadID>)*" <ThreadIDList> ::= "<ThreadID>(,<ThreadID>)*"
<TimeList> ::= "<Time>(,<Time>)*" <TimeList> ::= "<Time>(,<Time>)*"
@@ -1161,8 +1163,8 @@ Specify a collection of items by directly specifying them; the item type is dete
<SiteACLScopeList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector> <SiteACLScopeList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
<SiteEntity> ::= <SiteEntity> ::=
<SiteList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector> <SiteList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
<TasklistIDEntity> ::= <TasklistEntity> ::=
<TasklistIDList> | <FileSelector> | <CSVFileSelector> <TasklistIDList> | <TaskListTitleList> | <FileSelector> | <CSVFileSelector>
<TasklistIDTaskIDEntity> ::= <TasklistIDTaskIDEntity> ::=
<TasklistIDTaskIDList> | <FileSelector> | <CSVFileSelector> <TasklistIDTaskIDList> | <FileSelector> | <CSVFileSelector>
<ThreadIDEntity> ::= <ThreadIDEntity> ::=
@@ -7393,7 +7395,7 @@ gam <UserTypeEntity> show profile
(status needsaction|completed)| (status needsaction|completed)|
(due <Time>) (due <Time>)
gam <UserTypeEntity> create task <TasklistIDEntity> gam <UserTypeEntity> create task <TasklistEntity>
<TaskAttribute>* [parent <TaskID>] [previous <TaskID>] <TaskAttribute>* [parent <TaskID>] [previous <TaskID>]
[compact|formatjson|returnidonly] [compact|formatjson|returnidonly]
gam <UserTypeEntity> update task <TasklistIDTaskIDEntity> gam <UserTypeEntity> update task <TasklistIDTaskIDEntity>
@@ -7406,14 +7408,14 @@ gam <UserTypeEntity> move task <TasklistIDTaskIDEntity>
gam <UserTypeEntity> info task <TasklistIDTaskIDEntity> gam <UserTypeEntity> info task <TasklistIDTaskIDEntity>
[compact|formatjson] [compact|formatjson]
gam <UserTypeEntity> show tasks [tasklists <TasklistIDEntity>] gam <UserTypeEntity> show tasks [tasklists <TasklistEntity>]
[completedmin <Time>] [completedmax <Time>] [completedmin <Time>] [completedmax <Time>]
[duemin <Time>] [duemax <Time>] [duemin <Time>] [duemax <Time>]
[updatedmin <Time>] [updatedmin <Time>]
[showcompleted [<Boolean>]] [showdeleted [<Boolean>]] [showhidden [<Boolean>]] [showall] [showcompleted [<Boolean>]] [showdeleted [<Boolean>]] [showhidden [<Boolean>]] [showall]
[orderby completed|due|updated] [orderby completed|due|updated]
[countsonly|compact|formatjson] [countsonly|compact|formatjson]
gam <UserTypeEntity> print tasks [tasklists <TasklistIDEntity>] [todrive <ToDriveAttribute>*] gam <UserTypeEntity> print tasks [tasklists <TasklistEntity>] [todrive <ToDriveAttribute>*]
[completedmin <Time>] [completedmax <Time>] [completedmin <Time>] [completedmax <Time>]
[duemin <Time>] [duemax <Time>] [duemin <Time>] [duemax <Time>]
[updatedmin <Time>] [updatedmin <Time>]
@@ -7427,13 +7429,13 @@ gam <UserTypeEntity> print tasks [tasklists <TasklistIDEntity>] [todrive <ToDriv
gam <UserTypeEntity> create tasklist gam <UserTypeEntity> create tasklist
<TasklistAttribute>* <TasklistAttribute>*
[returnidonly] [formatjson] [returnidonly] [formatjson]
gam <UserTypeEntity> update tasklist <TasklistIDEntity> gam <UserTypeEntity> update tasklist <TasklistEntity>
<TasklistAttribute>* <TasklistAttribute>*
[formatjson] [formatjson]
gam <UserTypeEntity> delete tasklist <TasklistIDEntity> gam <UserTypeEntity> delete tasklist <TasklistEntity>
gam <UserTypeEntity> clear tasklist <TasklistIDEntity> gam <UserTypeEntity> clear tasklist <TasklistEntity>
gam <UserTypeEntity> info tasklist <TasklistIDEntity> gam <UserTypeEntity> info tasklist <TasklistEntity>
[formatjson] [formatjson]
gam <UserTypeEntity> show tasklists gam <UserTypeEntity> show tasklists
[countsonly|formatjson] [countsonly|formatjson]

View File

@@ -1,3 +1,32 @@
7.00.00
Merged GAM-Team version
6.65.00
Fixed bug in `gam <UserTypeEntity> create contact <JSONData>` that caused a trap when
contacts were being copied from one user to another.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-People-Contacts-Profiles#copy-user-contacts-to-another-user
Updated the following commands to allow specification of a task list by its title.
```
<TaskListTitle> ::= tltitle:<String>
<TasklistTitleList> ::= "'<TasklistTitle>'(,'<TasklistTitle>')*"
<TasklistEntity> ::=
<TasklistIDList> | <TaskListTitleList> | <FileSelector> | <CSVFileSelector>
gam <UserTypeEntity> create task <TasklistEntity>
gam <UserTypeEntity> show tasks [tasklists <TasklistEntity>]
gam <UserTypeEntity> print tasks [tasklists <TasklistEntity>]
gam <UserTypeEntity> update tasklist <TasklistEntity>
gam <UserTypeEntity> delete tasklist <TasklistEntity>
gam <UserTypeEntity> clear tasklist <TasklistEntity>
gam <UserTypeEntity> info tasklist <TasklistEntity>
```
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Tasks#specifying-task-lists
6.64.16 6.64.16
Fixed bug in `gam <UserTypeEntity> create task <TasklistIDEntity>` that caused a trap Fixed bug in `gam <UserTypeEntity> create task <TasklistIDEntity>` that caused a trap
@@ -20,6 +49,7 @@ Added command to get chrome app details.
``` ```
gam info appdetails android|chrome|web <AppID> [formatjson] gam info appdetails android|chrome|web <AppID> [formatjson]
``` ```
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Chrome-Installed-Apps
6.64.12 6.64.12
@@ -72,10 +102,6 @@ gam <UserTypeEntity> print events <UserCalendarEntity> [<EventEntity>]
gam <UserTypeEntity> update calattendees <UserCalendarEntity> <EventEntity> gam <UserTypeEntity> update calattendees <UserCalendarEntity> <EventEntity>
``` ```
7.00.00
Merged GAM-Team version
6.64.04 6.64.04
Updated `gam calendars <CalendarEntity> move events` and `gam <UserTypeEntity> move events <UserCalendarEntity>` Updated `gam calendars <CalendarEntity> move events` and `gam <UserTypeEntity> move events <UserCalendarEntity>`

View File

@@ -20146,7 +20146,19 @@ class PeopleManager():
unknownArgumentExit() unknownArgumentExit()
contactGroupsLists[PEOPLE_REMOVE_GROUPS_LIST].append(getString(Cmd.OB_STRING)) contactGroupsLists[PEOPLE_REMOVE_GROUPS_LIST].append(getString(Cmd.OB_STRING))
elif fieldName == PEOPLE_JSON: elif fieldName == PEOPLE_JSON:
person.update(getJSON(['resourceName', 'etag', 'metadata', PEOPLE_COVER_PHOTOS, PEOPLE_PHOTOS, PEOPLE_UPDATE_TIME])) jsonData = getJSON(['resourceName', 'etag', 'metadata', PEOPLE_COVER_PHOTOS, PEOPLE_PHOTOS, PEOPLE_UPDATE_TIME])
for membership in jsonData.pop('memberships', []):
contactGroupName = membership.get('contactGroupMembership', {}).get('contactGroupName', '')
if contactGroupName:
contactGroupsLists[PEOPLE_GROUPS_LIST].append(contactGroupName)
newClientData = []
for clientData in jsonData.pop('clientData', []):
if clientData['key'] not in {'ContactId', 'CtsContactHash'}:
newClientData.append({'key': clientData['key'], 'value': clientData['value']})
if newClientData:
person.setdefault(PEOPLE_CLIENT_DATA, [])
person[PEOPLE_CLIENT_DATA].extend(newClientData)
person.update(jsonData)
return (person, set(person.keys()), contactGroupsLists) return (person, set(person.keys()), contactGroupsLists)
PEOPLE_GROUP_ARGUMENT_TO_PROPERTY_MAP = { PEOPLE_GROUP_ARGUMENT_TO_PROPERTY_MAP = {
@@ -20195,7 +20207,9 @@ class PeopleManager():
contactGroup.setdefault(fieldName, []) contactGroup.setdefault(fieldName, [])
contactGroup[fieldName].append(entry) contactGroup[fieldName].append(entry)
elif fieldName == PEOPLE_JSON: elif fieldName == PEOPLE_JSON:
contactGroup.update(getJSON(['resourceName', 'etag', 'metadata', 'groupType', 'formattedName', 'memberResourceNames', 'memberCount'])) jsonData = getJSON(['resourceName', 'etag', 'metadata', 'formattedName', 'memberResourceNames', 'memberCount'])
if jsonData.get('groupType', '') != 'SYSTEM_CONTACT_GROUP':
contactGroup[PEOPLE_GROUP_NAME] = jsonData['name']
return (contactGroup, ','.join(contactGroup.keys())) return (contactGroup, ','.join(contactGroup.keys()))
PEOPLE_DIRECTORY_SOURCES_CHOICE_MAP = { PEOPLE_DIRECTORY_SOURCES_CHOICE_MAP = {
@@ -21917,6 +21931,8 @@ def createUserPeopleContactGroup(users):
entityType = Ent.USER entityType = Ent.USER
parameters = {'csvPF': None, 'titles': ['User', 'resourceName'], 'addCSVData': {}, 'returnIdOnly': False} parameters = {'csvPF': None, 'titles': ['User', 'resourceName'], 'addCSVData': {}, 'returnIdOnly': False}
body, _ = peopleManager.GetContactGroupFields(parameters) body, _ = peopleManager.GetContactGroupFields(parameters)
if PEOPLE_GROUP_NAME not in body:
return
csvPF = parameters['csvPF'] csvPF = parameters['csvPF']
addCSVData = parameters['addCSVData'] addCSVData = parameters['addCSVData']
if addCSVData: if addCSVData:
@@ -68998,6 +69014,33 @@ def deleteNotesACLs(users):
entityServiceNotApplicableWarning(Ent.USER, user, i, count) entityServiceNotApplicableWarning(Ent.USER, user, i, count)
break break
def getTaskLists(svc, user, i, count):
try:
results = callGAPIpages(svc.tasklists(), 'list', 'items',
pageMessage=getPageMessageForWhom(),
throwReasons=GAPI.TASKLIST_THROW_REASONS,
maxResults=100)
except GAPI.notFound:
results = []
except (GAPI.badRequest, GAPI.invalid) as e:
entityActionFailedWarning([Ent.USER, user, Ent.TASKLIST, None], str(e), i, count)
results = None
except GAPI.serviceNotAvailable:
entityServiceNotApplicableWarning(Ent.USER, user, i, count)
results = None
return results
def getTaskListIDfromTitle(svc, userTasklists, title, user, i, count):
if userTasklists is None:
printGettingEntityItemForWhom(Ent.TASKLIST, user, i, count)
userTasklists = getTaskLists(svc, user, i, count)
if userTasklists is None:
return None, None
for userTasklist in userTasklists:
if userTasklist['title'] == title:
return userTasklists, userTasklist['id']
return userTasklists, None
TASK_SKIP_OBJECTS = ['selfLink'] TASK_SKIP_OBJECTS = ['selfLink']
TASK_TIME_OBJECTS = ['due', 'completed', 'updated'] TASK_TIME_OBJECTS = ['due', 'completed', 'updated']
@@ -69048,7 +69091,7 @@ def getTaskMoveAttribute(myarg, kwargs):
return False return False
return True return True
# gam <UserTypeEntity> create task <TasklistIDEntity> # gam <UserTypeEntity> create task <TasklistEntity>
# <TaskAttribute>* [parent <TaskID>] [previous <TaskID>] # <TaskAttribute>* [parent <TaskID>] [previous <TaskID>]
# [compact|formatjson|returnidonly] # [compact|formatjson|returnidonly]
# gam <UserTypeEntity> update task <TasklistIDTaskIDEntity> # gam <UserTypeEntity> update task <TasklistIDTaskIDEntity>
@@ -69063,9 +69106,9 @@ def getTaskMoveAttribute(myarg, kwargs):
def processTasks(users): def processTasks(users):
action = Act.Get() action = Act.Get()
if action != Act.CREATE: if action != Act.CREATE:
tasklistTaskEntity = getUserObjectEntity(Cmd.OB_TASKLIST_ID_ENTITY, Ent.TASK) tasklistTaskEntity = getUserObjectEntity(Cmd.OB_TASKLIST_ID_TASK_ID_ENTITY, Ent.TASK, shlexSplit=True)
else: else:
tasklistTaskEntity = getUserObjectEntity(Cmd.OB_TASKLIST_ID_TASK_ID_ENTITY, Ent.TASK) tasklistTaskEntity = getUserObjectEntity(Cmd.OB_TASKLIST_ID_ENTITY, Ent.TASK, shlexSplit=True)
if action in {Act.DELETE, Act.CLEAR}: if action in {Act.DELETE, Act.CLEAR}:
FJQC = None FJQC = None
checkForExtraneousArguments() checkForExtraneousArguments()
@@ -69093,6 +69136,7 @@ def processTasks(users):
api=API.TASKS, showAction=FJQC is None or not FJQC.formatJSON) api=API.TASKS, showAction=FJQC is None or not FJQC.formatJSON)
if jcount == 0: if jcount == 0:
continue continue
userTasklists = None
Ind.Increment() Ind.Increment()
j = 0 j = 0
for tasklistTask in tasklistTasks: for tasklistTask in tasklistTasks:
@@ -69104,6 +69148,12 @@ def processTasks(users):
else: else:
tasklist = tasklistTask tasklist = tasklistTask
task = body.get('title', '') task = body.get('title', '')
if tasklist.startswith('tltitle:'):
tasklistTitle = tasklist[8:]
userTasklists, tasklist = getTaskListIDfromTitle(svc, userTasklists, tasklistTitle, user, i, count)
if tasklist is None:
entityActionFailedWarning([Ent.USER, user, Ent.TASKLIST, tasklistTitle, Ent.TASK, task], Msg.TASKLIST_TITLE_NOT_FOUND, j, jcount)
continue
try: try:
if action == Act.DELETE: if action == Act.DELETE:
callGAPI(svc.tasks(), 'delete', callGAPI(svc.tasks(), 'delete',
@@ -69163,14 +69213,14 @@ TASK_QUERY_STATE_MAP = {
'showhidden': 'showHidden', 'showhidden': 'showHidden',
} }
# gam <UserTypeEntity> show tasks [tasklists <TasklistIDEntity>] # gam <UserTypeEntity> show tasks [tasklists <TasklistEntity>]
# [completedmin <Time>] [completedmax <Time>] # [completedmin <Time>] [completedmax <Time>]
# [duemin <Time>] [duemax <Time>] # [duemin <Time>] [duemax <Time>]
# [updatedmin <Time>] # [updatedmin <Time>]
# [showcompleted [<Boolean>]] [showdeleted [<Boolean>]] [showhidden [<Boolean>]] [showall] # [showcompleted [<Boolean>]] [showdeleted [<Boolean>]] [showhidden [<Boolean>]] [showall]
# [orderby completed|due|updated] # [orderby completed|due|updated]
# [countsonly|compact|formatjson] # [countsonly|compact|formatjson]
# gam <UserTypeEntity> print tasks [tasklists <TasklistIDEntity>] [todrive <ToDriveAttribute>*] # gam <UserTypeEntity> print tasks [tasklists <TasklistEntity>] [todrive <ToDriveAttribute>*]
# [completedmin <Time>] [completedmax <Time>] # [completedmin <Time>] [completedmax <Time>]
# [duemin <Time>] [duemax <Time>] # [duemin <Time>] [duemax <Time>]
# [updatedmin <Time>] # [updatedmin <Time>]
@@ -69217,7 +69267,6 @@ def printShowTasks(users):
CSVTitle = 'Tasks' CSVTitle = 'Tasks'
FJQC = FormatJSONQuoteChar(csvPF) FJQC = FormatJSONQuoteChar(csvPF)
tasklistEntity = None tasklistEntity = None
tlkwargs = {'maxResults': 100}
kwargs = {'maxResults': 100} kwargs = {'maxResults': 100}
compact = countsOnly = False compact = countsOnly = False
orderBy = orderByNoDataValue = None orderBy = orderByNoDataValue = None
@@ -69226,7 +69275,7 @@ def printShowTasks(users):
if csvPF and myarg == 'todrive': if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters() csvPF.GetTodriveParameters()
elif myarg in {'tasklist', 'tasklists'}: elif myarg in {'tasklist', 'tasklists'}:
tasklistEntity = getUserObjectEntity(Cmd.OB_TASKLIST_ID_ENTITY, Ent.TASKLIST) tasklistEntity = getUserObjectEntity(Cmd.OB_TASKLIST_ID_ENTITY, Ent.TASKLIST, shlexSplit=True)
elif myarg in TASK_QUERY_TIME_MAP: elif myarg in TASK_QUERY_TIME_MAP:
kwargs[TASK_QUERY_TIME_MAP[myarg]] = getTimeOrDeltaFromNow() kwargs[TASK_QUERY_TIME_MAP[myarg]] = getTimeOrDeltaFromNow()
elif myarg in TASK_QUERY_STATE_MAP: elif myarg in TASK_QUERY_STATE_MAP:
@@ -69254,22 +69303,13 @@ def printShowTasks(users):
if not svc: if not svc:
continue continue
printGettingEntityItemForWhom(Ent.TASKLIST, user, i, count) printGettingEntityItemForWhom(Ent.TASKLIST, user, i, count)
try: results = getTaskLists(svc, user, i, count)
results = callGAPIpages(svc.tasklists(), 'list', 'items', if results is None:
pageMessage=getPageMessageForWhom(),
throwReasons=GAPI.TASKLIST_THROW_REASONS,
**tlkwargs)
except GAPI.notFound:
results = []
except (GAPI.badRequest, GAPI.invalid) as e:
entityActionFailedWarning([Ent.USER, user, Ent.TASKLIST, None], str(e), i, count)
continue
except GAPI.serviceNotAvailable:
entityServiceNotApplicableWarning(Ent.USER, user, i, count)
continue continue
tasklists = [tasklist['id'] for tasklist in results] tasklists = [tasklist['id'] for tasklist in results]
jcount = len(tasklists) jcount = len(tasklists)
else: else:
userTasklists = None
user, svc, tasklists, jcount = _validateUserGetObjectList(user, i, count, tasklistEntity, api=API.TASKS, user, svc, tasklists, jcount = _validateUserGetObjectList(user, i, count, tasklistEntity, api=API.TASKS,
showAction=FJQC is None or not FJQC.formatJSON) showAction=FJQC is None or not FJQC.formatJSON)
if jcount == 0: if jcount == 0:
@@ -69281,6 +69321,12 @@ def printShowTasks(users):
j = 0 j = 0
for tasklist in tasklists: for tasklist in tasklists:
j += 1 j += 1
if tasklist.startswith('tltitle:'):
tasklistTitle = tasklist[8:]
userTasklists, tasklist = getTaskListIDfromTitle(svc, userTasklists, tasklistTitle, user, i, count)
if tasklist is None:
entityActionFailedWarning([Ent.USER, user, Ent.TASKLIST, tasklistTitle], Msg.TASKLIST_TITLE_NOT_FOUND, j, jcount)
continue
printGettingEntityItemForWhom(Ent.TASK, tasklist, j, jcount) printGettingEntityItemForWhom(Ent.TASK, tasklist, j, jcount)
try: try:
tasks = callGAPIpages(svc.tasks(), 'list', 'items', tasks = callGAPIpages(svc.tasks(), 'list', 'items',
@@ -69353,17 +69399,17 @@ def _showTasklist(tasklist, j=0, jcount=0, FJQC=None):
# gam <UserTypeEntity> create tasklist # gam <UserTypeEntity> create tasklist
# [title <String>] # [title <String>]
# [returnidonly] [formatjson] # [returnidonly] [formatjson]
# gam <UserTypeEntity> update tasklist <TasklistIDEntity> # gam <UserTypeEntity> update tasklist <TasklistEntity>
# [title <String>] # [title <String>]
# [formatjson] # [formatjson]
# gam <UserTypeEntity> info tasklist <TasklistIDEntity> # gam <UserTypeEntity> info tasklist <TasklistEntity>
# [formatjson] # [formatjson]
# gam <UserTypeEntity> delete tasklist <TasklistIDEntity> # gam <UserTypeEntity> delete tasklist <TasklistEntity>
# gam <UserTypeEntity> clear tasklist <TasklistIDEntity> # gam <UserTypeEntity> clear tasklist <TasklistEntity>
def processTasklists(users): def processTasklists(users):
action = Act.Get() action = Act.Get()
if action != Act.CREATE: if action != Act.CREATE:
tasklistEntity = getUserObjectEntity(Cmd.OB_TASKLIST_ID_ENTITY, Ent.TASKLIST) tasklistEntity = getUserObjectEntity(Cmd.OB_TASKLIST_ID_ENTITY, Ent.TASKLIST, shlexSplit=True)
else: else:
tasklistEntity = {'item': Ent.TASKLIST, 'list': [None], 'dict': None} tasklistEntity = {'item': Ent.TASKLIST, 'list': [None], 'dict': None}
if action in {Act.DELETE, Act.CLEAR}: if action in {Act.DELETE, Act.CLEAR}:
@@ -69384,6 +69430,7 @@ def processTasklists(users):
i, count, users = getEntityArgument(users) i, count, users = getEntityArgument(users)
for user in users: for user in users:
i += 1 i += 1
userTasklists = None
user, svc, tasklists, jcount = _validateUserGetObjectList(user, i, count, tasklistEntity, user, svc, tasklists, jcount = _validateUserGetObjectList(user, i, count, tasklistEntity,
api=API.TASKS, api=API.TASKS,
showAction=action != Act.CREATE and (FJQC is None or not FJQC.formatJSON)) showAction=action != Act.CREATE and (FJQC is None or not FJQC.formatJSON))
@@ -69393,6 +69440,13 @@ def processTasklists(users):
j = 0 j = 0
for tasklist in tasklists: for tasklist in tasklists:
j += 1 j += 1
if action != Act.CREATE:
if tasklist.startswith('tltitle:'):
tasklistTitle = tasklist[8:]
userTasklists, tasklist = getTaskListIDfromTitle(svc, userTasklists, tasklistTitle, user, i, count)
if tasklist is None:
entityActionFailedWarning([Ent.USER, user, Ent.TASKLIST, tasklistTitle], Msg.TASKLIST_TITLE_NOT_FOUND, j, jcount)
continue
try: try:
if action == Act.DELETE: if action == Act.DELETE:
callGAPI(svc.tasklists(), 'delete', callGAPI(svc.tasklists(), 'delete',
@@ -69445,7 +69499,6 @@ def printShowTasklists(users):
CSVTitle = 'TaskLists' CSVTitle = 'TaskLists'
FJQC = FormatJSONQuoteChar(csvPF) FJQC = FormatJSONQuoteChar(csvPF)
countsOnly = False countsOnly = False
kwargs = {'maxResults': 100}
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if csvPF and myarg == 'todrive': if csvPF and myarg == 'todrive':
@@ -69463,18 +69516,8 @@ def printShowTasklists(users):
if not svc: if not svc:
continue continue
printGettingAllEntityItemsForWhom(Ent.TASKLIST, user, i, count) printGettingAllEntityItemsForWhom(Ent.TASKLIST, user, i, count)
try: tasklists = getTaskLists(svc, user, i, count)
tasklists = callGAPIpages(svc.tasklists(), 'list', 'items', if tasklists is None:
pageMessage=getPageMessageForWhom(),
throwReasons=GAPI.TASKLIST_THROW_REASONS,
**kwargs)
except GAPI.notFound:
tasklists = []
except (GAPI.badRequest, GAPI.invalid) as e:
entityActionFailedWarning([Ent.USER, user, Ent.TASKLIST, None], str(e), i, count)
continue
except GAPI.serviceNotAvailable:
entityServiceNotApplicableWarning(Ent.USER, user, i, count)
continue continue
jcount = len(tasklists) jcount = len(tasklists)
if countsOnly: if countsOnly: