Rebranded license SKU 1010470004; student group updates

This commit is contained in:
Ross Scroggs
2025-09-03 12:12:40 -07:00
parent f4aed33e30
commit 6dbdc4db07
5 changed files with 97 additions and 76 deletions

View File

@@ -275,8 +275,8 @@ If an item contains spaces, it should be surrounded by ".
colabpro | 1010500001 | Colab Pro | colabpro | 1010500001 | Colab Pro |
colabpro+ | colabproplus | 1010500002 | Colab Pro+ | colabpro+ | colabproplus | 1010500002 | Colab Pro+ |
eeu | 1010490001 | SKU Endpoint Education Upgrade | eeu | 1010490001 | SKU Endpoint Education Upgrade |
gaiproedu | geminiedu | 1010470004 | Google AI Pro for Education |
geminibiz | 1010470003 | Gemini Business | geminibiz | 1010470003 | Gemini Business |
geminiedu | 1010470004 | Gemini Education |
geminiedupremium| 1010470005 | Gemini Education Premium | geminiedupremium| 1010470005 | Gemini Education Premium |
geminient| duetai | 1010470001 | Gemini Enterprise | geminient| duetai | 1010470001 | Gemini Enterprise |
geminiultra | 1010470008 | Google AI Ultra for Business | geminiultra | 1010470008 | Google AI Ultra for Business |
@@ -316,12 +316,12 @@ If an item contains spaces, it should be surrounded by ".
wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 | Google Workspace Business Starter | wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 | Google Workspace Business Starter |
wsbizstarterarchived | workspacebusinessstarterarchived | 1010340005 | Google Workspace Business Starter - Archived User | wsbizstarterarchived | workspacebusinessstarterarchived | 1010340005 | Google Workspace Business Starter - Archived User |
wsentess | workspaceenterpriseessentials | 1010060003 | Google Workspace Enterprise Essentials | wsentess | workspaceenterpriseessentials | 1010060003 | Google Workspace Enterprise Essentials |
wsentplus | workspaceenterpriseplus | gae | gse | enterprise | gsuiteenterprise | 1010020020 | Google Workspace Enterprise Plus | wsentplus | workspaceenterpriseplus | gae | gse | enterprise | gsuiteenterprise | 1010020020 | Google Workspace Enterprise Plus (formerly G Suite Enterprise) |
wsentstan | workspaceenterprisestandard | 1010020026 | Google Workspace Enterprise Standard | wsentstan | workspaceenterprisestandard | 1010020026 | Google Workspace Enterprise Standard |
wsentstanarchived | workspaceenterprisestandardarchived | 1010340004 | Google Workspace Enterprise Standard - Archived User | wsentstanarchived | workspaceenterprisestandardarchived | 1010340004 | Google Workspace Enterprise Standard - Archived User |
wsentstarter | workspaceenterprisestarter | wes | 1010020029 | Workspace Enterprise Starter | wsentstarter | workspaceenterprisestarter | wes | 1010020029 | Workspace Enterprise Starter |
wsess | workspaceesentials | gsuiteessentials | essentials | d4e | driveenterprise | drive4enterprise | 1010060001 | Google Workspace Essentials | wsess | workspaceesentials | gsuiteessentials | essentials | d4e | driveenterprise | drive4enterprise | 1010060001 | Google Workspace Essentials (formerly G Suite Essentials) |
wsessplus | workspaceessentialsplus | 1010060005 | Google Workspace Essentials Plus | wsessplus | workspaceessentialsplus | 1010060005 | Google Workspace Enterprise Essentials Plus |
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030 | Google Workspace Frontline Starter | wsflw | workspacefrontline | workspacefrontlineworker | 1010020030 | Google Workspace Frontline Starter |
wsflwstan | workspacefrontlinestan | workspacefrontlineworkerstan | 1010020031 | Google Workspace Frontline Standard | wsflwstan | workspacefrontlinestan | workspacefrontlineworkerstan | 1010020031 | Google Workspace Frontline Standard |
wsflwplus | workspacefrontlineplus | workspacefrontlineworkerplus | 1010020034 | Google Workspace Frontline Plus wsflwplus | workspacefrontlineplus | workspacefrontlineworkerplus | 1010020034 | Google Workspace Frontline Plus
@@ -5096,7 +5096,7 @@ gam create|add drivefileacl <SharedDriveEntityAdmin>
anyone|(user <UserItem>)|(group <GroupItem>)|(domain <DomainName>) anyone|(user <UserItem>)|(group <GroupItem>)|(domain <DomainName>)
(role <DriveFileACLRole>) [withlink|(allowfilediscovery|discoverable [<Boolean>])] (role <DriveFileACLRole>) [withlink|(allowfilediscovery|discoverable [<Boolean>])]
(mappermissionsdomain <DomainName> <DomainName>)* (mappermissionsdomain <DomainName> <DomainName>)*
[expiration <Time>] [sendemail] [emailmessage <String>] [expiration <Time>] [sendemail|sendnotification] [emailmessage <String>]
[showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
gam update drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail> gam update drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail>
(role <DriveFileACLRole>) [expires|expiration <Time>] [removeexpiration [<Boolean>]] (role <DriveFileACLRole>) [expires|expiration <Time>] [removeexpiration [<Boolean>]]
@@ -5123,7 +5123,7 @@ gam print drivefileacls <SharedDriveEntityAdmin> [todrive <ToDriveAttribute>*]
(orderby <DriveFileOrderByFieldName> [ascending|descending])* (orderby <DriveFileOrderByFieldName> [ascending|descending])*
[formatjson [quotechar <Character>]] [formatjson [quotechar <Character>]]
gam create|add permissions <SharedDriveEntityAdmin> <DriveFilePermissionEntity> gam create|add permissions <SharedDriveEntityAdmin> <DriveFilePermissionEntity>
[expires|expiration <Time>] [sendemail] [emailmessage <String>] [expires|expiration <Time>] [sendemail|sendnotification] [emailmessage <String>]
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
gam delete permissions <SharedDriveEntityAdmin> <DriveFilePermissionIDEntity> gam delete permissions <SharedDriveEntityAdmin> <DriveFilePermissionIDEntity>
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
@@ -5136,7 +5136,7 @@ gam <UserTypeEntity> create|add drivefileacl <SharedDriveEntityAdmin>
(role <DriveFileACLRole>) [withlink|(allowfilediscovery|discoverable [<Boolean>])] (role <DriveFileACLRole>) [withlink|(allowfilediscovery|discoverable [<Boolean>])]
(mappermissionsdomain <DomainName> <DomainName>)* (mappermissionsdomain <DomainName> <DomainName>)*
[movetonewownersroot [<Boolean>]] [movetonewownersroot [<Boolean>]]
[expiration <Time>] [sendemail] [emailmessage <String>] [expiration <Time>] [sendemail|sendnotification] [emailmessage <String>]
[showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
adminaccess adminaccess
gam <UserTypeEntity> update drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail> gam <UserTypeEntity> update drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail>
@@ -5167,7 +5167,7 @@ gam <UserTypeEntity> print drivefileacls <SharedDriveEntityAdmin> [todrive <ToDr
(orderby <DriveFileOrderByFieldName> [ascending|descending])* (orderby <DriveFileOrderByFieldName> [ascending|descending])*
[formatjson [quotechar <Character>]] [formatjson [quotechar <Character>]]
gam <UserTypeEntity> create|add permissions <SharedDriveEntityAdmin> <DriveFilePermissionEntity> gam <UserTypeEntity> create|add permissions <SharedDriveEntityAdmin> <DriveFilePermissionEntity>
[expires|expiration <Time>] [sendemail] [emailmessage <String>] adminaccess [expires|expiration <Time>] [sendemail|sendnotification] [emailmessage <String>] adminaccess
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
gam <UserTypeEntity> delete permissions <SharedDriveEntityAdmin> <DriveFilePermissionIDEntity> adminaccess gam <UserTypeEntity> delete permissions <SharedDriveEntityAdmin> <DriveFilePermissionIDEntity> adminaccess
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
@@ -6896,7 +6896,7 @@ gam <UserTypeEntity> create|add drivefileacl <DriveFileEntity> [adminaccess|asad
anyone|(user <UserItem>)|(group <GroupItem>)|(domain <DomainName>) (role <DriveFileACLRole>) anyone|(user <UserItem>)|(group <GroupItem>)|(domain <DomainName>) (role <DriveFileACLRole>)
[withlink|(allowfilediscovery|discoverable [<Boolean>])] [expiration <Time>] [withlink|(allowfilediscovery|discoverable [<Boolean>])] [expiration <Time>]
[moveToNewOwnersRoot [<Boolean>]] [moveToNewOwnersRoot [<Boolean>]]
[sendemail] [emailmessage <String>] [sendemail|sendnotification] [emailmessage <String>]
[updatesheetprotectedranges [<Boolean>]] [updatesheetprotectedranges [<Boolean>]]
[showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
gam <UserTypeEntity> update drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail> gam <UserTypeEntity> update drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail>
@@ -6922,7 +6922,7 @@ gam <UserTypeEntity> print drivefileacls <DriveFileEntity> [todrive <ToDriveAttr
(orderby <DriveFileOrderByFieldName> [ascending|descending])* (orderby <DriveFileOrderByFieldName> [ascending|descending])*
[formatjson [quotechar <Character>]] [formatjson [quotechar <Character>]]
gam <UserTypeEntity> create|add permissions <DriveFileEntity> <DriveFilePermissionEntity> gam <UserTypeEntity> create|add permissions <DriveFileEntity> <DriveFilePermissionEntity>
[expires|expiration <Time>] [sendemail] [emailmessage <String>] [expires|expiration <Time>] [sendemail|sendnotification] [emailmessage <String>]
[movetonewownersroot [<Boolean>]] [movetonewownersroot [<Boolean>]]
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
gam <UserTypeEntity> delete permissions <DriveFileEntity> <DriveFilePermissionIDEntity> gam <UserTypeEntity> delete permissions <DriveFileEntity> <DriveFilePermissionIDEntity>

View File

@@ -1,3 +1,9 @@
7.20.03
Rebranded license SKU `1010470004` from `Gemini Education` to `Google AI Pro for Education`.
Additional updates to student groups in Google Classroom.
7.20.02 7.20.02
Upgraded `gam create course-studentgroups` to allow specification of multiple student group titles; Upgraded `gam create course-studentgroups` to allow specification of multiple student group titles;

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
""" """
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>' __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.20.02' __version__ = '7.20.03'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
#pylint: disable=wrong-import-position #pylint: disable=wrong-import-position
@@ -51247,7 +51247,7 @@ def doCreateCourseStudentGroups():
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg == 'title': if myarg == 'title':
titles.append(getString(Cmd.OB_STRING)) titles.append(getString(Cmd.OB_STRING, maxLen=100))
elif myarg == 'select': elif myarg == 'select':
titles.extend(getEntityList(Cmd.OB_STRING_ENTITY, shlexSplit=True)) titles.extend(getEntityList(Cmd.OB_STRING_ENTITY, shlexSplit=True))
elif _getCourseSelectionParameters(myarg, courseSelectionParameters): elif _getCourseSelectionParameters(myarg, courseSelectionParameters):
@@ -51515,7 +51515,7 @@ def doPrintCourseStudentGroups(showMembers=False):
row['JSON'] = json.dumps(cleanJSON(studentGroup), ensure_ascii=False, sort_keys=False) row['JSON'] = json.dumps(cleanJSON(studentGroup), ensure_ascii=False, sort_keys=False)
csvPF.WriteRowTitles(row) csvPF.WriteRowTitles(row)
continue continue
printGettingEntityItemForWhom(Ent.USER, formatKeyValueList('', [Ent.Singular(Ent.COURSE_STUDENTGROUP), studentGroupId], printGettingEntityItemForWhom(Ent.STUDENT, formatKeyValueList('', [Ent.Singular(Ent.COURSE_STUDENTGROUP), studentGroupId],
currentCount(i, count))) currentCount(i, count)))
pageMessage = getPageMessage() pageMessage = getPageMessage()
try: try:
@@ -51562,8 +51562,32 @@ def doPrintCourseStudentGroups(showMembers=False):
# gam sync course-studentgroup-members <CourseID> <StudentGroupID> <UserTypeEntity> # gam sync course-studentgroup-members <CourseID> <StudentGroupID> <UserTypeEntity>
# gam clear course-studentgroup-members <CourseID> <StudentGroupID> # gam clear course-studentgroup-members <CourseID> <StudentGroupID>
def doProcessCourseStudentGroupMembers(): def doProcessCourseStudentGroupMembers():
def _getCurrentStudents(): def _getCourseStudents():
printGettingEntityItemForWhom(Ent.USER, Ent.Singular(Ent.COURSE_STUDENTGROUP), studentGroupId) studentIdEmailMap = {}
studentEmailIdMap = {}
printGettingEntityItemForWhom(Ent.STUDENT, formatKeyValueList('', [Ent.Singular(Ent.COURSE), courseId], ''))
pageMessage = getPageMessage()
try:
students = callGAPIpages(ocroom.courses().students(), 'list', 'students',
pageMessage=pageMessage,
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE,
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
courseId=courseId, fields='nextPageToken,students(profile(id,emailAddress))',
pageSize=GC.Values[GC.CLASSROOM_MAX_RESULTS])
for student in students:
studentIdEmailMap[student['profile']['id']] = student['profile']['emailAddress'].lower()
studentEmailIdMap[student['profile']['emailAddress'].lower()] = student['profile']['id']
return (studentIdEmailMap, studentEmailIdMap)
except GAPI.notFound as e:
entityActionFailedExit([Ent.COURSE, courseId, studentGroupId], str(e))
except (GAPI.serviceNotAvailable, GAPI.notImplemented) as e:
entityActionFailedExit([Ent.COURSE, courseId], str(e))
except (GAPI.forbidden, GAPI.permissionDenied) as e:
ClientAPIAccessDeniedExit(str(e))
def _getGroupCurrentStudents():
printGettingEntityItemForWhom(Ent.STUDENT, formatKeyValueList('', [Ent.Singular(Ent.COURSE_STUDENTGROUP), studentGroupId], ''))
pageMessage = getPageMessage() pageMessage = getPageMessage()
try: try:
return callGAPIpages(ocroom.courses().studentGroups().studentGroupMembers(), 'list', 'studentGroupMembers', return callGAPIpages(ocroom.courses().studentGroups().studentGroupMembers(), 'list', 'studentGroupMembers',
@@ -51581,18 +51605,24 @@ def doProcessCourseStudentGroupMembers():
except (GAPI.forbidden, GAPI.permissionDenied) as e: except (GAPI.forbidden, GAPI.permissionDenied) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
def _getStudentUserId(kvList, student, i, count): def _validateClStudents(clStudents):
normalizedEmailAddressOrUID = normalizeEmailAddressOrUID(student) status = True
if normalizedEmailAddressOrUID.find('@') == -1: clStudentIds = []
return normalizedEmailAddressOrUID count = len(clStudents)
try: i = 0
return callGAPI(cd.users(), 'get', kvList = [Ent.COURSE, courseId, Ent.STUDENT, '']
throwReasons=GAPI.USER_GET_THROW_REASONS, for student in clStudents:
userKey=normalizedEmailAddressOrUID, fields='id')['id'] i += 1
except (GAPI.userNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden, student = normalizeEmailAddressOrUID(student)
GAPI.badRequest, GAPI.backendError, GAPI.systemError) as e: if student in studentIdEmailMap:
entityActionFailedWarning(kvList, str(e), i, count) clStudentIds.append(student)
return None elif student in studentEmailIdMap:
clStudentIds.append(studentEmailIdMap[student])
else:
kvList[-1] = student
entityActionFailedWarning(kvList, Msg.STUDENT_NOT_IN_COURSE, i, count)
status = False
return clStudentIds if status else None
def _processStudent(function, kvList, kwargs, i, count): def _processStudent(function, kvList, kwargs, i, count):
try: try:
@@ -51611,85 +51641,69 @@ def doProcessCourseStudentGroupMembers():
except (GAPI.forbidden, GAPI.permissionDenied) as e: except (GAPI.forbidden, GAPI.permissionDenied) as e:
ClientAPIAccessDeniedExit(str(e)) ClientAPIAccessDeniedExit(str(e))
def _addStudents(students, getUserIds): def _addStudents(students):
count = len(students) count = len(students)
i = 0 i = 0
entityPerformActionNumItems([Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId], count, Ent.USER) entityPerformActionNumItems([Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId], count, Ent.STUDENT)
kvList = [Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId, Ent.USER, ''] kvList = [Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId, Ent.STUDENT, '']
kwargs = {'courseId': courseId, 'studentGroupId': studentGroupId, 'body': {'userId': ''}} kwargs = {'courseId': courseId, 'studentGroupId': studentGroupId, 'body': {'userId': ''}}
Ind.Increment()
for student in students: for student in students:
i += 1 i += 1
if getUserIds: kvList[-1] = studentIdEmailMap[student]
userId = _getStudentUserId(kvList, student, i, count) kwargs['body']['userId'] = student
if userId is None:
continue
kvList[-1] = student
else:
userId = student
kvList[-1] = convertUIDtoEmailAddress(f"id:{userId}", cd=cd, emailTypes=['user'])
kwargs['body']['userId'] = userId
_processStudent('create', kvList, kwargs, i, count) _processStudent('create', kvList, kwargs, i, count)
Ind.Decrement()
def _removeStudents(students, getUserIds): def _removeStudents(students):
count = len(students) count = len(students)
i = 0 i = 0
entityPerformActionNumItems([Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId], count, Ent.USER) entityPerformActionNumItems([Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId], count, Ent.STUDENT)
kvList = [Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId, Ent.USER, ''] kvList = [Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId, Ent.STUDENT, '']
kwargs = {'courseId': courseId, 'studentGroupId': studentGroupId, 'userId': ''} kwargs = {'courseId': courseId, 'studentGroupId': studentGroupId, 'userId': ''}
Ind.Increment()
for student in students: for student in students:
i += 1 i += 1
if getUserIds: kvList[-1] = studentIdEmailMap[student]
userId = _getStudentUserId(kvList, student, i, count) kwargs['userId'] = student
if userId is None:
continue
kvList[-1] = student
else:
userId = student
kvList[-1] = convertUIDtoEmailAddress(f"id:{userId}", cd=cd, emailTypes=['user'])
kwargs['userId'] = userId
_processStudent('delete', kvList, kwargs, i, count) _processStudent('delete', kvList, kwargs, i, count)
Ind.Decrement()
croom = buildGAPIObject(API.CLASSROOM) croom = buildGAPIObject(API.CLASSROOM)
cd = buildGAPIObject(API.DIRECTORY)
action = Act.Get() action = Act.Get()
courseId = getString(Cmd.OB_COURSE_ID) courseId = getString(Cmd.OB_COURSE_ID)
studentGroupId = getString(Cmd.OB_STUDENTGROUP_ID) studentGroupId = getString(Cmd.OB_STUDENTGROUP_ID)
if action != Act.CLEAR: if action != Act.CLEAR:
_, clStudents = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS, groupMemberType=Ent.TYPE_USER) _, clStudents = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS, groupMemberType=Ent.TYPE_USER)
clStudents = [normalizeEmailAddressOrUID(student) for student in clStudents] clStudentIds = []
checkForExtraneousArguments() checkForExtraneousArguments()
_, count, coursesInfo = _getCoursesOwnerInfo(croom, [courseId], GC.Values[GC.USE_COURSE_OWNER_ACCESS]) _, count, coursesInfo = _getCoursesOwnerInfo(croom, [courseId], GC.Values[GC.USE_COURSE_OWNER_ACCESS])
if count == 0: if count == 0:
return return
ocroom = coursesInfo[courseId]['croom'] ocroom = coursesInfo[courseId]['croom']
courseId = coursesInfo[courseId]['id'] courseId = coursesInfo[courseId]['id']
studentIdEmailMap, studentEmailIdMap = _getCourseStudents()
if action in {Act.SYNC, Act.CLEAR}: if action in {Act.SYNC, Act.CLEAR}:
currentStudents = [student['userId'] for student in _getCurrentStudents()] currentStudents = [student['userId'] for student in _getGroupCurrentStudents()]
if action != Act.CLEAR:
clStudentIds = _validateClStudents(clStudents)
if clStudentIds is None:
return
if action == Act.CLEAR: if action == Act.CLEAR:
_removeStudents(currentStudents, False) _removeStudents(currentStudents)
elif action == Act.DELETE: elif action == Act.DELETE:
_removeStudents(clStudents, True) _removeStudents(clStudentIds)
elif action in {Act.ADD, Act.CREATE}: elif action in {Act.ADD, Act.CREATE}:
_addStudents(clStudents, True) _addStudents(clStudentIds)
else: # elif action == Act.SYNC: else: # elif action == Act.SYNC:
currentMembersSet = set(currentStudents) currentMembersSet = set(currentStudents)
syncMembersSet = set() syncMembersSet = set(clStudentIds)
count = len(clStudents)
i = 0
kvList = [Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId, Ent.USER, '']
for student in clStudents:
i += 1
kvList[-1] = student
userId = _getStudentUserId(kvList, student, i, count)
if userId is None:
continue
syncMembersSet.add(userId)
removeStudentsSet = currentMembersSet-syncMembersSet removeStudentsSet = currentMembersSet-syncMembersSet
addStudentsSet = syncMembersSet-currentMembersSet addStudentsSet = syncMembersSet-currentMembersSet
Act.Set(Act.DELETE) Act.Set(Act.DELETE)
_removeStudents(removeStudentsSet, False) _removeStudents(removeStudentsSet)
Act.Set(Act.ADD) Act.Set(Act.ADD)
_addStudents(addStudentsSet, False) _addStudents(addStudentsSet)
# gam print course-studentgroup-members [todrive <ToDriveAttribute>*] # gam print course-studentgroup-members [todrive <ToDriveAttribute>*]
# (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>]) # (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
@@ -65073,7 +65087,7 @@ def _checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess):
# (mappermissionsdomain <DomainName> <DomainName>)* # (mappermissionsdomain <DomainName> <DomainName>)*
# [moveToNewOwnersRoot [<Boolean>]] # [moveToNewOwnersRoot [<Boolean>]]
# [updatesheetprotectedranges [<Boolean>]] # [updatesheetprotectedranges [<Boolean>]]
# [sendemail] [emailmessage <String>] # [sendemail|sendnotification] [emailmessage <String>]
# [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] # [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
def createDriveFileACL(users, useDomainAdminAccess=False): def createDriveFileACL(users, useDomainAdminAccess=False):
moveToNewOwnersRoot = False moveToNewOwnersRoot = False
@@ -65117,7 +65131,7 @@ def createDriveFileACL(users, useDomainAdminAccess=False):
elif myarg in {'expiration', 'expires'}: elif myarg in {'expiration', 'expires'}:
expirationLocation = Cmd.Location() expirationLocation = Cmd.Location()
body['expirationTime'] = getTimeOrDeltaFromNow() body['expirationTime'] = getTimeOrDeltaFromNow()
elif myarg == 'sendemail': elif myarg in {'sendemail', 'sendnotification'}:
sendNotificationEmail = True sendNotificationEmail = True
elif myarg == 'emailmessage': elif myarg == 'emailmessage':
sendNotificationEmail = True sendNotificationEmail = True
@@ -65373,7 +65387,7 @@ def doUpdateDriveFileACLs():
updateDriveFileACLs([_getAdminEmail()], True) updateDriveFileACLs([_getAdminEmail()], True)
# gam [<UserTypeEntity>] create permissions <DriveFileEntity> <DriveFilePermissionsEntity> [asadmin] # gam [<UserTypeEntity>] create permissions <DriveFileEntity> <DriveFilePermissionsEntity> [asadmin]
# [expiration <Time>] [sendmail] [emailmessage <String>] # [expiration <Time>] [sendemail|sendnotification] [emailmessage <String>]
# [moveToNewOwnersRoot [<Boolean>]] # [moveToNewOwnersRoot [<Boolean>]]
# <PermissionMatch>* [<PermissionMatchAction>] # <PermissionMatch>* [<PermissionMatchAction>]
def createDriveFilePermissions(users, useDomainAdminAccess=False): def createDriveFilePermissions(users, useDomainAdminAccess=False):
@@ -65491,7 +65505,7 @@ def createDriveFilePermissions(users, useDomainAdminAccess=False):
moveToNewOwnersRoot = getBoolean() moveToNewOwnersRoot = getBoolean()
elif myarg in {'expiration', 'expires'}: elif myarg in {'expiration', 'expires'}:
expiration = getTimeOrDeltaFromNow() expiration = getTimeOrDeltaFromNow()
elif myarg == 'sendemail': elif myarg in {'sendemail', 'sendnotification'}:
sendNotificationEmail = True sendNotificationEmail = True
elif myarg == 'emailmessage': elif myarg == 'emailmessage':
sendNotificationEmail = True sendNotificationEmail = True

View File

@@ -499,6 +499,7 @@ STATISTICS_MOVE_FILE = 'Total: {0}, Moved: {1}, Shortcut created {2}, Shortcut e
STATISTICS_MOVE_FOLDER = 'Total: {0}, Moved: {1}, Shortcut created {2}, Shortcut exists {3}, Duplicate: {4}, Merged: {5}, Move Failed: {6}, Not writable: {7}' STATISTICS_MOVE_FOLDER = 'Total: {0}, Moved: {1}, Shortcut created {2}, Shortcut exists {3}, Duplicate: {4}, Merged: {5}, Move Failed: {6}, Not writable: {7}'
STATISTICS_USER_NOT_ORGANIZER = 'User not organizer: {0}' STATISTICS_USER_NOT_ORGANIZER = 'User not organizer: {0}'
STRING_LENGTH = 'string length' STRING_LENGTH = 'string length'
STUDENT_NOT_IN_COURSE = 'Student not in course'
SUBKEY_FIELD_MISMATCH = 'subkeyfield {0} does not match saved subkeyfield {1}' SUBKEY_FIELD_MISMATCH = 'subkeyfield {0} does not match saved subkeyfield {1}'
SUBSCRIPTION_NOT_FOUND = 'Could not find subscription' SUBSCRIPTION_NOT_FOUND = 'Could not find subscription'
SUFFIX_NOT_ALLOWED_WITH_CUSTOMLANGUAGE = 'Suffix {0} not allowed with customLanguage {1}' SUFFIX_NOT_ALLOWED_WITH_CUSTOMLANGUAGE = 'Suffix {0} not allowed with customLanguage {1}'

View File

@@ -100,7 +100,7 @@ _SKUS = {
'1010470003': { '1010470003': {
'product': '101047', 'aliases': ['geminibiz'], 'displayName': 'Gemini Business'}, 'product': '101047', 'aliases': ['geminibiz'], 'displayName': 'Gemini Business'},
'1010470004': { '1010470004': {
'product': '101047', 'aliases': ['geminiedu'], 'displayName': 'Gemini Education'}, 'product': '101047', 'aliases': ['gaiproedu', 'geminiedu'], 'displayName': 'Google AI Pro for Education'},
'1010470005': { '1010470005': {
'product': '101047', 'aliases': ['geminiedupremium'], 'displayName': 'Gemini Education Premium'}, 'product': '101047', 'aliases': ['geminiedupremium'], 'displayName': 'Gemini Education Premium'},
'1010470006': { '1010470006': {