gam7 7.10.9__py3-none-any.whl → 7.11.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gam7 might be problematic. Click here for more details.

gam/__init__.py CHANGED
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
25
25
  """
26
26
 
27
27
  __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
28
- __version__ = '7.10.09'
28
+ __version__ = '7.11.00'
29
29
  __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
30
30
 
31
31
  #pylint: disable=wrong-import-position
@@ -46776,6 +46776,13 @@ COURSE_STATE_MAPS = {
46776
46776
  'published': 'PUBLISHED',
46777
46777
  'deleted': 'DELETED',
46778
46778
  },
46779
+ Cmd.OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST: {
46780
+ 'draft': 'DRAFT',
46781
+ 'published': 'PUBLISHED',
46782
+ },
46783
+ Cmd.OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST: {
46784
+ 'published': 'PUBLISHED',
46785
+ },
46779
46786
  Cmd.OB_COURSE_WORK_STATE_LIST: {
46780
46787
  'draft': 'DRAFT',
46781
46788
  'published': 'PUBLISHED',
@@ -48923,7 +48930,7 @@ def doPrintCourseParticipants():
48923
48930
  csvPF.SetSortTitles(COURSE_PARTICIPANTS_SORT_TITLES)
48924
48931
  csvPF.writeCSVfile('Course Participants')
48925
48932
 
48926
- def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
48933
+ def _batchAddItemsToCourse(croom, courseId, i, count, addItems, addType):
48927
48934
  _ADD_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
48928
48935
  GAPI.ALREADY_EXISTS: Msg.DUPLICATE,
48929
48936
  GAPI.FAILED_PRECONDITION: Msg.NOT_ALLOWED}
@@ -48939,7 +48946,7 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
48939
48946
  else:
48940
48947
  errMsg = getHTTPError(_ADD_PART_REASON_TO_MESSAGE_MAP, http_status, reason, message)
48941
48948
  if (reason == GAPI.PERMISSION_DENIED) and (ri[RI_ROLE] in {Ent.STUDENT, Ent.TEACHER}) and ('CannotDirectAddUser' in errMsg):
48942
- errMsg += f' Add external user with: gam user {ri[RI_ITEM]} create classroominvitation courses {ri[RI_ENTITY]} role {Ent.Singular(ri[RI_ROLE])}'
48949
+ errMsg += f' Add external user with: gam user {ri[RI_ITEM]} create classroominvitation courses {ri[RI_ENTITY]} addType {Ent.Singular(ri[RI_ROLE])}'
48943
48950
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
48944
48951
  return
48945
48952
  waitOnFailure(1, 10, reason, message)
@@ -48961,39 +48968,44 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
48961
48968
  except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
48962
48969
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
48963
48970
 
48964
- if role == Ent.STUDENT:
48971
+ if addType == Ent.STUDENT:
48965
48972
  service = croom.courses().students()
48966
48973
  attribute = 'userId'
48967
- elif role == Ent.TEACHER:
48974
+ elif addType == Ent.TEACHER:
48968
48975
  service = croom.courses().teachers()
48969
48976
  attribute = 'userId'
48970
- elif role == Ent.COURSE_ALIAS:
48977
+ elif addType == Ent.COURSE_ALIAS:
48971
48978
  service = croom.courses().aliases()
48972
48979
  attribute = 'alias'
48973
- else: # role == Ent.COURSE_TOPIC:
48980
+ elif addType == Ent.COURSE_TOPIC:
48974
48981
  service = croom.courses().topics()
48975
48982
  attribute = 'name'
48983
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
48984
+ service = croom.courses().announcements()
48985
+ attribute = 'text'
48976
48986
  method = getattr(service, 'create')
48977
48987
  Act.Set(Act.ADD)
48978
- jcount = len(addParticipants)
48988
+ jcount = len(addItems)
48979
48989
  noScopeCourseId = removeCourseIdScope(courseId)
48980
- entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, role, i, count)
48990
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, addType, i, count)
48981
48991
  Ind.Increment()
48982
48992
  svcargs = dict([('courseId', courseId), ('body', {attribute: None}), ('fields', '')]+GM.Globals[GM.EXTRA_ARGS_LIST])
48983
48993
  dbatch = croom.new_batch_http_request(callback=_callbackAddItemsToCourse)
48984
48994
  bcount = 0
48985
48995
  j = 0
48986
- for participant in addParticipants:
48996
+ for addItem in addItems:
48987
48997
  j += 1
48988
48998
  svcparms = svcargs.copy()
48989
- if role in {Ent.STUDENT, Ent.TEACHER}:
48990
- svcparms['body'][attribute] = cleanItem = normalizeEmailAddressOrUID(participant)
48991
- elif role == Ent.COURSE_ALIAS:
48992
- svcparms['body'][attribute] = addCourseAliasScope(participant)
48999
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49000
+ svcparms['body'][attribute] = cleanItem = normalizeEmailAddressOrUID(addItem)
49001
+ elif addType == Ent.COURSE_ALIAS:
49002
+ svcparms['body'][attribute] = addCourseAliasScope(addItem)
48993
49003
  cleanItem = removeCourseAliasScope(svcparms['body'][attribute])
48994
- else: # role == Ent.COURSE_TOPIC:
48995
- svcparms['body'][attribute] = cleanItem = participant
48996
- dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, role))
49004
+ elif addType == Ent.COURSE_TOPIC:
49005
+ svcparms['body'][attribute] = cleanItem = addItem
49006
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49007
+ svcparms['body'] = cleanItem = addItem
49008
+ dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, addType))
48997
49009
  bcount += 1
48998
49010
  if bcount >= GC.Values[GC.BATCH_SIZE]:
48999
49011
  executeBatch(dbatch)
@@ -49003,7 +49015,7 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
49003
49015
  dbatch.execute()
49004
49016
  Ind.Decrement()
49005
49017
 
49006
- def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, role):
49018
+ def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeItems, removeType):
49007
49019
  _REMOVE_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
49008
49020
  GAPI.FORBIDDEN: Msg.FORBIDDEN,
49009
49021
  GAPI.PERMISSION_DENIED: Msg.PERMISSION_DENIED}
@@ -49024,7 +49036,7 @@ def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, r
49024
49036
  try:
49025
49037
  callGAPI(service, 'delete',
49026
49038
  throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
49027
- GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
49039
+ GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE, GAPI.FAILED_PRECONDITION],
49028
49040
  retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE], triesLimit=0 if reason != GAPI.NOT_FOUND else 3,
49029
49041
  courseId=addCourseIdScope(ri[RI_ENTITY]),
49030
49042
  body={attribute: ri[RI_ITEM] if ri[RI_ROLE] != Ent.COURSE_ALIAS else addCourseAliasScope(ri[RI_ITEM])},
@@ -49035,42 +49047,47 @@ def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, r
49035
49047
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.FORBIDDEN, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49036
49048
  except GAPI.permissionDenied:
49037
49049
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.PERMISSION_DENIED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49038
- except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
49050
+ except (GAPI.quotaExceeded, GAPI.serviceNotAvailable, GAPI.failedPrecondition) as e:
49039
49051
  entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
49040
49052
 
49041
- if role == Ent.STUDENT:
49053
+ if removeType == Ent.STUDENT:
49042
49054
  service = croom.courses().students()
49043
49055
  attribute = 'userId'
49044
- elif role == Ent.TEACHER:
49056
+ elif removeType == Ent.TEACHER:
49045
49057
  service = croom.courses().teachers()
49046
49058
  attribute = 'userId'
49047
- elif role == Ent.COURSE_ALIAS:
49059
+ elif removeType == Ent.COURSE_ALIAS:
49048
49060
  service = croom.courses().aliases()
49049
49061
  attribute = 'alias'
49050
- else: # role == Ent.COURSE_TOPIC:
49062
+ elif removeType == Ent.COURSE_TOPIC:
49051
49063
  service = croom.courses().topics()
49052
49064
  attribute = 'id'
49065
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49066
+ service = croom.courses().announcements()
49067
+ attribute = 'id'
49053
49068
  method = getattr(service, 'delete')
49054
49069
  Act.Set(Act.REMOVE)
49055
- jcount = len(removeParticipants)
49070
+ jcount = len(removeItems)
49056
49071
  noScopeCourseId = removeCourseIdScope(courseId)
49057
- entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, role, i, count)
49072
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, removeType, i, count)
49058
49073
  Ind.Increment()
49059
49074
  svcargs = dict([('courseId', courseId), ('fields', ''), (attribute, None)]+GM.Globals[GM.EXTRA_ARGS_LIST])
49060
49075
  dbatch = croom.new_batch_http_request(callback=_callbackRemoveItemsFromCourse)
49061
49076
  bcount = 0
49062
49077
  j = 0
49063
- for participant in removeParticipants:
49078
+ for removeItem in removeItems:
49064
49079
  j += 1
49065
49080
  svcparms = svcargs.copy()
49066
- if role in {Ent.STUDENT, Ent.TEACHER}:
49067
- svcparms[attribute] = cleanItem = normalizeEmailAddressOrUID(participant)
49068
- elif role == Ent.COURSE_ALIAS:
49069
- svcparms[attribute] = addCourseAliasScope(participant)
49081
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49082
+ svcparms[attribute] = cleanItem = normalizeEmailAddressOrUID(removeItem)
49083
+ elif removeType == Ent.COURSE_ALIAS:
49084
+ svcparms[attribute] = addCourseAliasScope(removeItem)
49070
49085
  cleanItem = removeCourseAliasScope(svcparms[attribute])
49071
- else: # role == Ent.COURSE_TOPIC:
49072
- svcparms[attribute] = cleanItem = participant
49073
- dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, role))
49086
+ elif removeType == Ent.COURSE_TOPIC:
49087
+ svcparms[attribute] = cleanItem = removeItem
49088
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49089
+ svcparms[attribute] = cleanItem = removeItem
49090
+ dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, removeType))
49074
49091
  bcount += 1
49075
49092
  if bcount >= GC.Values[GC.BATCH_SIZE]:
49076
49093
  executeBatch(dbatch)
@@ -49100,9 +49117,28 @@ def _updateCourseOwner(croom, courseId, owner, i, count):
49100
49117
  entityActionPerformedMessage([Ent.COURSE, removeCourseIdScope(courseId), Ent.TEACHER, owner], Msg.ALREADY_WAS_OWNER, i, count)
49101
49118
  Act.Set(action)
49102
49119
 
49103
- ADD_REMOVE_PARTICIPANT_TYPES_MAP = {
49120
+ def getCourseAnnouncement(createCmd):
49121
+ body = {}
49122
+ while Cmd.ArgumentsRemaining():
49123
+ myarg = getArgument()
49124
+ if myarg == 'text':
49125
+ body['text'] = getString(Cmd.OB_STRING, minLen=1, maxLen=30000)
49126
+ elif myarg == 'scheduledtime':
49127
+ body['scheduledTime'] = getTimeOrDeltaFromNow()
49128
+ elif myarg == 'state':
49129
+ body['state'] = getChoice(COURSE_STATE_MAPS[Cmd.OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST if createCmd else Cmd.OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST],
49130
+ mapChoice=True)
49131
+ else:
49132
+ unknownArgumentExit()
49133
+ if createCmd and 'text' not in body:
49134
+ missingArgumentExit('text <String>')
49135
+ return body
49136
+
49137
+ ADD_REMOVE_UPDATE_ITEM_TYPES_MAP = {
49104
49138
  'alias': Ent.COURSE_ALIAS,
49105
49139
  'aliases': Ent.COURSE_ALIAS,
49140
+ 'announcement': Ent.COURSE_ANNOUNCEMENT,
49141
+ 'announcements': Ent.COURSE_ANNOUNCEMENT,
49106
49142
  'student': Ent.STUDENT,
49107
49143
  'students': Ent.STUDENT,
49108
49144
  'teacher': Ent.TEACHER,
@@ -49123,6 +49159,10 @@ PARTICIPANT_EN_MAP = {
49123
49159
 
49124
49160
  # gam courses <CourseEntity> create alias <CourseAliasEntity>
49125
49161
  # gam course <CourseID> create alias <CourseAlias>
49162
+ # gam courses <CourseEntity> create announcement
49163
+ # text <String> [scheduledtime <Time>] [state draft|published]
49164
+ # gam course <CourseID> create announcement
49165
+ # text <String> [scheduledtime <Time>] [state draft|published]
49126
49166
  # gam courses <CourseEntity> create topic <CourseTopicEntity>
49127
49167
  # gam course <CourseID> create topic <CourseTopic>
49128
49168
  # gam courses <CourseEntity> create students <UserTypeEntity>
@@ -49131,35 +49171,39 @@ PARTICIPANT_EN_MAP = {
49131
49171
  # gam course <CourseID> create teacher [makefirstteacherowner] <EmailAddress>
49132
49172
  def doCourseAddItems(courseIdList, getEntityListArg):
49133
49173
  croom = buildGAPIObject(API.CLASSROOM)
49134
- role = getChoice(ADD_REMOVE_PARTICIPANT_TYPES_MAP, mapChoice=True)
49135
- if role == Ent.TEACHER:
49174
+ addType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49175
+ if addType == Ent.TEACHER:
49136
49176
  makeFirstTeacherOwner = checkArgumentPresent(['makefirstteacherowner'])
49137
49177
  else:
49138
49178
  makeFirstTeacherOwner = False
49139
49179
  if not getEntityListArg:
49140
- if role in {Ent.STUDENT, Ent.TEACHER}:
49180
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49141
49181
  addItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
49142
- elif role == Ent.COURSE_ALIAS:
49182
+ elif addType == Ent.COURSE_ALIAS:
49143
49183
  addItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
49144
- else: # role == Ent.COURSE_TOPIC:
49184
+ elif addType == Ent.COURSE_TOPIC:
49145
49185
  addItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC)
49186
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49187
+ addItems = [getCourseAnnouncement(True)]
49146
49188
  courseParticipantLists = None
49147
49189
  else:
49148
- if role in {Ent.STUDENT, Ent.TEACHER}:
49190
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49149
49191
  _, addItems = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS,
49150
- typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[role]},
49192
+ typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[addType]},
49151
49193
  isSuspended=False, isArchived=False)
49152
- elif role == Ent.COURSE_ALIAS:
49194
+ elif addType == Ent.COURSE_ALIAS:
49153
49195
  addItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
49154
- else: # role == Ent.COURSE_TOPIC:
49196
+ elif addType == Ent.COURSE_TOPIC:
49155
49197
  addItems = getEntityList(Cmd.OB_COURSE_TOPIC_ENTITY, shlexSplit=True)
49198
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49199
+ addItems = getCourseAnnouncement(True)
49156
49200
  courseParticipantLists = addItems if isinstance(addItems, dict) else None
49157
49201
  if courseParticipantLists is None:
49158
49202
  firstTeacher = None
49159
49203
  if makeFirstTeacherOwner and addItems:
49160
49204
  firstTeacher = normalizeEmailAddressOrUID(addItems[0])
49161
49205
  checkForExtraneousArguments()
49162
- i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, role == Ent.COURSE_TOPIC,
49206
+ i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, addType in {Ent.COURSE_TOPIC, Ent.COURSE_ANNOUNCEMENT},
49163
49207
  addCIIdScope=courseParticipantLists is None)
49164
49208
  for courseId, courseInfo in coursesInfo.items():
49165
49209
  i += 1
@@ -49169,43 +49213,51 @@ def doCourseAddItems(courseIdList, getEntityListArg):
49169
49213
  if makeFirstTeacherOwner and addItems:
49170
49214
  firstTeacher = normalizeEmailAddressOrUID(addItems[0])
49171
49215
  courseId = addCourseIdScope(courseId)
49172
- _batchAddItemsToCourse(courseInfo['croom'], courseId, i, count, addItems, role)
49216
+ _batchAddItemsToCourse(courseInfo['croom'], courseId, i, count, addItems, addType)
49173
49217
  if makeFirstTeacherOwner and firstTeacher:
49174
49218
  _updateCourseOwner(courseInfo['croom'], courseId, firstTeacher, i, count)
49175
49219
 
49176
49220
  # gam courses <CourseEntity> remove alias <CourseAliasEntity>
49177
49221
  # gam course <CourseID> remove alias <CourseAlias>
49222
+ # gam courses <CourseEntity> remove announcement <CourseAnnouncementIDEntity>
49223
+ # gam course <CourseID> remove announcement <CourseAnnouncementID>
49178
49224
  # gam courses <CourseEntity> remove topic <CourseTopicIDEntity>
49179
49225
  # gam course <CourseID> remove topic <CourseTopicID>
49180
49226
  # gam courses <CourseEntity> remove teachers|students [owneracccess] <UserTypeEntity>
49181
49227
  # gam course <CourseID> remove teacher|student [owneracccess] <EmailAddress>
49182
49228
  def doCourseRemoveItems(courseIdList, getEntityListArg):
49183
49229
  croom = buildGAPIObject(API.CLASSROOM)
49184
- role = getChoice(ADD_REMOVE_PARTICIPANT_TYPES_MAP, mapChoice=True)
49230
+ removeType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49185
49231
  if not getEntityListArg:
49186
- if role in {Ent.STUDENT, Ent.TEACHER}:
49232
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49187
49233
  useOwnerAccess = GC.Values[GC.USE_COURSE_OWNER_ACCESS]
49188
49234
  if checkArgumentPresent(OWNER_ACCESS_OPTIONS):
49189
49235
  useOwnerAccess = True
49190
49236
  removeItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
49191
- elif role == Ent.COURSE_ALIAS:
49237
+ elif removeType == Ent.COURSE_ALIAS:
49192
49238
  useOwnerAccess = False
49193
49239
  removeItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
49194
- else: # role == Ent.COURSE_TOPIC:
49240
+ elif removeType == Ent.COURSE_TOPIC:
49195
49241
  useOwnerAccess = True
49196
49242
  removeItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
49243
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49244
+ useOwnerAccess = True
49245
+ removeItems = getStringReturnInList(Cmd.OB_COURSE_ANNOUNCEMENT_ID)
49197
49246
  courseParticipantLists = None
49198
49247
  else:
49199
- if role in {Ent.STUDENT, Ent.TEACHER}:
49248
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49200
49249
  useOwnerAccess = checkArgumentPresent(OWNER_ACCESS_OPTIONS)
49201
49250
  _, removeItems = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS,
49202
- typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[role]})
49203
- elif role == Ent.COURSE_ALIAS:
49251
+ typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[removeType]})
49252
+ elif removeType == Ent.COURSE_ALIAS:
49204
49253
  useOwnerAccess = False
49205
49254
  removeItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
49206
- else: # role == Ent.COURSE_TOPIC:
49255
+ elif removeType == Ent.COURSE_TOPIC:
49207
49256
  useOwnerAccess = True
49208
49257
  removeItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
49258
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49259
+ useOwnerAccess = True
49260
+ removeItems = getEntityList(Cmd.OB_COURSE_ANNOUNCEMENT_ID_ENTITY, shlexSplit=True)
49209
49261
  courseParticipantLists = removeItems if isinstance(removeItems, dict) else None
49210
49262
  checkForExtraneousArguments()
49211
49263
  i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, useOwnerAccess,
@@ -49215,7 +49267,72 @@ def doCourseRemoveItems(courseIdList, getEntityListArg):
49215
49267
  if courseParticipantLists:
49216
49268
  removeItems = courseParticipantLists[courseId]
49217
49269
  courseId = addCourseIdScope(courseId)
49218
- _batchRemoveItemsFromCourse(courseInfo['croom'], courseId, i, count, removeItems, role)
49270
+ _batchRemoveItemsFromCourse(courseInfo['croom'], courseId, i, count, removeItems, removeType)
49271
+
49272
+ # gam courses <CourseEntity> update announcement <CourseAnnouncemntIDEntity>
49273
+ # [text <String>] [scheduledtime <Time>] [state published]
49274
+ # gam course <CourseID> update announcement <CourseAnnouncementID>
49275
+ # [text <String>] [scheduledtime <Time>] [state published]
49276
+ # gam courses <CourseEntity> update topic <CourseTopicIDEntity> <CourseTopic>
49277
+ # gam course <CourseID> update topic <CourseTopicID> <CourseTopic>
49278
+ def doCourseUpdateItems(courseIdList, getEntityListArg):
49279
+ croom = buildGAPIObject(API.CLASSROOM)
49280
+ updateType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49281
+ if not getEntityListArg:
49282
+ if updateType == Ent.COURSE_TOPIC:
49283
+ useOwnerAccess = True
49284
+ updateItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
49285
+ body = {'name': getString(Cmd.OB_COURSE_TOPIC)}
49286
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49287
+ useOwnerAccess = True
49288
+ updateItems = getStringReturnInList(Cmd.OB_COURSE_ANNOUNCEMENT_ID)
49289
+ body = getCourseAnnouncement(False)
49290
+ courseItemLists = None
49291
+ else:
49292
+ if updateType == Ent.COURSE_TOPIC:
49293
+ useOwnerAccess = True
49294
+ updateItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
49295
+ body = {'name': getString(Cmd.OB_COURSE_TOPIC)}
49296
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49297
+ useOwnerAccess = True
49298
+ updateItems = getEntityList(Cmd.OB_COURSE_ANNOUNCEMENT_ID_ENTITY, shlexSplit=True)
49299
+ body = getCourseAnnouncement(False)
49300
+ courseItemLists = updateItems if isinstance(updateItems, dict) else None
49301
+ checkForExtraneousArguments()
49302
+ i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, useOwnerAccess,
49303
+ addCIIdScope=courseItemLists is None)
49304
+ for courseId, courseInfo in coursesInfo.items():
49305
+ i += 1
49306
+ if courseItemLists:
49307
+ updateItems = courseItemLists[courseId]
49308
+ courseId = addCourseIdScope(courseId)
49309
+ jcount = len(updateItems)
49310
+ noScopeCourseId = removeCourseIdScope(courseId)
49311
+ if updateType == Ent.COURSE_TOPIC:
49312
+ service = courseInfo['croom'].courses().topics()
49313
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49314
+ service = courseInfo['croom'].courses().announcements()
49315
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, updateType, i, count)
49316
+ Ind.Increment()
49317
+ j = 0
49318
+ for updateItem in updateItems:
49319
+ j += 1
49320
+ try:
49321
+ callGAPI(service, 'patch',
49322
+ throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
49323
+ GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
49324
+ retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE],
49325
+ courseId=addCourseIdScope(courseId), id=updateItem, updateMask=','.join(body.keys()), body=body, fields='')
49326
+ entityActionPerformed([Ent.COURSE, courseId, updateType, updateItem], j, jcount)
49327
+ except GAPI.notFound:
49328
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.DOES_NOT_EXIST, j, jcount)
49329
+ except GAPI.forbidden:
49330
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.FORBIDDEN, j, jcount)
49331
+ except GAPI.permissionDenied:
49332
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.PERMISSION_DENIED, j, jcount)
49333
+ except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
49334
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], str(e), j, jcount)
49335
+ Ind.Decrement()
49219
49336
 
49220
49337
  # gam courses <CourseEntity> clear teachers|students
49221
49338
  # gam course <CourseID> clear teacher|student
@@ -65870,12 +65987,14 @@ SHAREDDRIVE_ACL_ROLES_MAP = {
65870
65987
  'writer': 'writer',
65871
65988
  }
65872
65989
 
65990
+ SHOWWEBVIEWLINK_CHOICES = {'text', 'hyperlink'}
65991
+
65873
65992
  # gam <UserTypeEntity> print shareddrives [todrive <ToDriveAttribute>*]
65874
65993
  # [asadmin [shareddriveadminquery|query <QuerySharedDrive>]]
65875
65994
  # [matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
65876
65995
  # (role|roles <SharedDriveACLRoleList>)*
65877
65996
  # [fields <SharedDriveFieldNameList>] [noorgunits [<Boolean>]]
65878
- # [showwebviewlink]
65997
+ # [showwebviewlink [text|hyperlink]]
65879
65998
  # [guiroles [<Boolean>]] [formatjson [quotechar <Character>]]
65880
65999
  # [showitemcountonly]
65881
66000
  # gam <UserTypeEntity> show shareddrives
@@ -65883,7 +66002,7 @@ SHAREDDRIVE_ACL_ROLES_MAP = {
65883
66002
  # [matchname <REMatchPattrn>] [orgunit|org|ou <OrgUnitPath>]
65884
66003
  # (role|roles <SharedDriveACLRoleLIst>)*
65885
66004
  # [fields <SharedDriveFieldNameList>] [noorgunits [<Boolean>]]
65886
- # [showwebviewlink]
66005
+ # [showwebviewlink [text|hyperlink]]
65887
66006
  # [guiroles [<Boolean>]] [formatjson]
65888
66007
  # [showitemcountonly]
65889
66008
  def printShowSharedDrives(users, useDomainAdminAccess=False):
@@ -65893,7 +66012,10 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
65893
66012
  if td_ouid:
65894
66013
  shareddrive['orgUnit'] = orgUnitIdToPathMap.get(f'id:{td_ouid}', UNKNOWN)
65895
66014
  if showWebViewLink:
65896
- shareddrive['webViewLink'] = 'https://drive.google.com/drive/folders/'+shareddrive['id']
66015
+ if showWebViewLink == 'text':
66016
+ shareddrive['webViewLink'] = 'https://drive.google.com/drive/folders/'+shareddrive['id']
66017
+ else:
66018
+ shareddrive['webViewLink'] = '=HYPERLINK("https://drive.google.com/drive/folders/'+shareddrive['id']+'", "'+shareddrive['name']+'")'
65897
66019
  if not showFields:
65898
66020
  return shareddrive
65899
66021
  sshareddrive = {}
@@ -65912,7 +66034,7 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
65912
66034
  showOrgUnitPaths = True
65913
66035
  orgUnitIdToPathMap = None
65914
66036
  guiRoles = showItemCountOnly = False
65915
- showWebViewLink = False
66037
+ showWebViewLink = ''
65916
66038
  while Cmd.ArgumentsRemaining():
65917
66039
  myarg = getArgument()
65918
66040
  if csvPF and myarg == 'todrive':
@@ -65943,7 +66065,7 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
65943
66065
  elif myarg == 'guiroles':
65944
66066
  guiRoles = getBoolean()
65945
66067
  elif myarg == 'showwebviewlink':
65946
- showWebViewLink = True
66068
+ showWebViewLink = getChoice(SHOWWEBVIEWLINK_CHOICES)
65947
66069
  elif myarg == 'showitemcountonly':
65948
66070
  showItemCountOnly = True
65949
66071
  showOrgUnitPaths = False
@@ -77160,6 +77282,7 @@ COURSE_SUBCOMMANDS = {
77160
77282
  'add': (Act.ADD, doCourseAddItems),
77161
77283
  'clear': (Act.REMOVE, doCourseClearParticipants),
77162
77284
  'remove': (Act.REMOVE, doCourseRemoveItems),
77285
+ 'update': (Act.UPDATE, doCourseUpdateItems),
77163
77286
  'sync': (Act.SYNC, doCourseSyncParticipants),
77164
77287
  }
77165
77288
 
gam/gamlib/glclargs.py CHANGED
@@ -875,8 +875,11 @@ class GamCLArgs():
875
875
  OB_CONTACT_GROUP_ITEM = 'ContactGroupItem'
876
876
  OB_COURSE_ALIAS = 'CourseAlias'
877
877
  OB_COURSE_ALIAS_ENTITY = 'CourseAliasEntity'
878
+ OB_COURSE_ANNOUNCEMENT_ID = "CourseAnnouncementID"
878
879
  OB_COURSE_ANNOUNCEMENT_ID_ENTITY = "CourseAnnouncementIDEntity"
879
880
  OB_COURSE_ANNOUNCEMENT_STATE_LIST = "CourseAnnouncementStateList"
881
+ OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST = "CourseAnnouncementAddStateList"
882
+ OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST = "CourseAnnouncementUpdateStateList"
880
883
  OB_COURSE_ENTITY = 'CourseEntity'
881
884
  OB_COURSE_ID = 'CourseID'
882
885
  OB_COURSE_MATERIAL_ID_ENTITY = 'CourseMaterialIDEntity'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gam7
3
- Version: 7.10.9
3
+ Version: 7.11.0
4
4
  Summary: CLI tool to manage Google Workspace
5
5
  Project-URL: Homepage, https://github.com/GAM-team/GAM
6
6
  Project-URL: Issues, https://github.com/GAM-team/GAM/issues
@@ -1,4 +1,4 @@
1
- gam/__init__.py,sha256=a5ax8oW9hLZp6ojUKdPmXBmRIKKA949pTP3H9OvJvoY,3535564
1
+ gam/__init__.py,sha256=LGOfPmsfDfMeTyC8wEv38P4K-9Tp5OQpvhBjul5puL4,3541795
2
2
  gam/__main__.py,sha256=amz0-959ph6zkZKqjaar4n60yho-T37w6qWI36qx0CA,1049
3
3
  gam/cacerts.pem,sha256=82Ak7btW_2XvocLUvAwPmpx8Chi0oqtZUG1gseLK_t4,50235
4
4
  gam/cbcm-v1.1beta1.json,sha256=xO5XloCQQULmPbFBx5bckdqmbLFQ7sJ2TImhE1ysDIY,19439
@@ -25,7 +25,7 @@ gam/gamlib/__init__.py,sha256=z5mF-y0j8pm-YNFBaiuxB4M_GAUPG-cXWwrhYwrVReM,679
25
25
  gam/gamlib/glaction.py,sha256=1Il_HrChVnPkzZwiZs5au4mFQVtq4K1Z42uIuR6qdnI,9419
26
26
  gam/gamlib/glapi.py,sha256=27NW2etvdNK4jfR699eqFzhV4gPotijXIQeX-wxCvHA,35319
27
27
  gam/gamlib/glcfg.py,sha256=bNTckxzIM_HruxO2DfYsDbEgqOIz1RX6CbU6XOQQQyg,28296
28
- gam/gamlib/glclargs.py,sha256=Ohe746FOQqMlXlutH-XJ6E1unYNzf_EJhdubnPp3new,42472
28
+ gam/gamlib/glclargs.py,sha256=Q0aaKTFYna18cdnPMrwQYThCEgYXgGby9UngnXBGZjY,42681
29
29
  gam/gamlib/glentity.py,sha256=c9-7MAp0HruXEUVq8Nwkllxc4KypFeZRUFkvVzPwrwk,33760
30
30
  gam/gamlib/glgapi.py,sha256=2mu7DQGGYmF7ZGXeBQu_mT1FNZgXbEQqcddJjHzbvE4,39833
31
31
  gam/gamlib/glgdata.py,sha256=weRppttWm6uRyqtBoGPKoHiNZ2h28nhfUV4J_mbCszY,2707
@@ -65,8 +65,8 @@ gam/googleapiclient/discovery_cache/base.py,sha256=yCDPtxnbNN-p5_9fzBacC6P3wcUPl
65
65
  gam/googleapiclient/discovery_cache/file_cache.py,sha256=sim3Mg4HgRYo3vX75jvcKy_aV568EvIrtBfvfbw-044,4774
66
66
  gam/iso8601/__init__.py,sha256=Z2PsYbXgAH5a5xzUvgczCboPzqWpm65kRcIngCnhViU,1218
67
67
  gam/iso8601/iso8601.py,sha256=Li2FHZ4sBTWuthuQhyCvmvj0j6At8JbGzkSv2fc2RHU,4384
68
- gam7-7.10.9.dist-info/METADATA,sha256=9YO3xc8QUSIb2qXdCLLFSbQDi0UcpQshJOXVhGhTfng,2978
69
- gam7-7.10.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
70
- gam7-7.10.9.dist-info/entry_points.txt,sha256=HVUM5J7dA8YwvJfG30jiLefR19ExMs387TWugWd9sf4,42
71
- gam7-7.10.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
72
- gam7-7.10.9.dist-info/RECORD,,
68
+ gam7-7.11.0.dist-info/METADATA,sha256=Gj9pwsrYzuf2IHM4qxcRWLPhz65GTySIa92YiFAaxjg,2978
69
+ gam7-7.11.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
70
+ gam7-7.11.0.dist-info/entry_points.txt,sha256=HVUM5J7dA8YwvJfG30jiLefR19ExMs387TWugWd9sf4,42
71
+ gam7-7.11.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
72
+ gam7-7.11.0.dist-info/RECORD,,
File without changes