From 3e7124946e1d10c712f4b72d9310dbc37829b7ef Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 14 Aug 2024 17:04:49 -0700 Subject: [PATCH] Cleaned up progress messages in `gam create|update course ... copyfrom`. --- docs/GamUpdates.md | 4 ++ docs/How-to-Upgrade-from-Standard-GAM.md | 4 +- ...TD3-securely-on-a-Google-Compute-Engine.md | 37 ++++++++-------- docs/Version-and-Help.md | 12 +++--- src/GamUpdate.txt | 4 ++ src/gam/__init__.py | 43 ++++++++++++------- src/gam/gamlib/glentity.py | 4 ++ 7 files changed, 66 insertions(+), 42 deletions(-) diff --git a/docs/GamUpdates.md b/docs/GamUpdates.md index 44e97896..fde43439 100644 --- a/docs/GamUpdates.md +++ b/docs/GamUpdates.md @@ -10,6 +10,10 @@ Add the `-s` option to the end of the above commands to suppress creating the `g See [Downloads-Installs](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads-Installs) for Windows or other options, including manual installation +### 6.80.04 + +Cleaned up progress messages in `gam create|update course ... copyfrom`. + ### 6.80.03 Added option `stripcrsfromname` to `gam print driveactivity` that causes carriage returns, diff --git a/docs/How-to-Upgrade-from-Standard-GAM.md b/docs/How-to-Upgrade-from-Standard-GAM.md index dbf87408..4fdba117 100644 --- a/docs/How-to-Upgrade-from-Standard-GAM.md +++ b/docs/How-to-Upgrade-from-Standard-GAM.md @@ -251,7 +251,7 @@ writes the credentials into the file oauth2.txt. admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt admin@server:/Users/admin$ gam version WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found -GAMADV-XTD3 6.80.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.80.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.4 64-bit final MacOS Sonoma 14.5 x86_64 @@ -923,7 +923,7 @@ writes the credentials into the file oauth2.txt. C:\>del C:\GAMConfig\oauth2.txt C:\>gam version WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found -GAMADV-XTD3 6.80.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.80.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.4 64-bit final Windows-10-10.0.17134 AMD64 diff --git a/docs/Running-GAMADV-XTD3-securely-on-a-Google-Compute-Engine.md b/docs/Running-GAMADV-XTD3-securely-on-a-Google-Compute-Engine.md index 2ad93ce3..ebbe3cd2 100644 --- a/docs/Running-GAMADV-XTD3-securely-on-a-Google-Compute-Engine.md +++ b/docs/Running-GAMADV-XTD3-securely-on-a-Google-Compute-Engine.md @@ -16,36 +16,37 @@ GAMADV-XTD3 version 6.50.00 or higher is required. 1. Create a [GCP project](https://cloud.google.com/resource-manager/docs/creating-managing-projects). 2. Create [a service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts) which will be used by GAMADV-XTD3. - * Enter a value in Service account name - * Enter text in Service account description - * Click Create and Continue - * Click Continue under Grant this service account access to project - * Click Done under Grant users access to this service account + * Enter a value in `Service account name` + * Enter text in `Service account description` + * Click `Create` and `Continue` + * Click `Continue` under `Grant this service account access to project` + * Click `Done` under `Grant users access to this service account` 3. Grant the service account rights to generate authentication tokens. * Go to [console.cloud.google.com](https://console.cloud.google.com). - * Go to "IAM & Admin" > Service accounts + * Go to `IAM & Admin` > `Service accounts` * Click on the service account you created (not the default service account). * Copy the email address of your service account to the clipboard. - * Click on the Permissions tab. - * Click "Grant Access". - * In the "New principals text box, paste the service account email you copied. - * Give your service account the "Service Account Key Admin", "Service Account Token Creator" and "View Service Accounts" roles. - * Click Save + * Click on the `Permissions` tab. + * Click `Grant Access`. + * In the `New principals` text box, paste the service account email you copied. + * Give your service account the `Service Account Token Creator` and `View Service Accounts` roles. + * Click `Save` 4. [Create a Windows or Linux virtual machine](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances). * Scroll down and start at Create a VM and attach the service account - * Click Go to VM instances - * Click Create Instance - * Enter a value for Name - * Configure Manage Tags and Labels + * Click `Go to VM instances` + * Click `Create Instance` + * Enter a value for `Name` + * Configure `Manage Tags and Labels` * You can choose a region physically close to you though you may be limited in your choices if you want to use the free tier. * GAMADV-XTD3 can run on the minimal `e2-micro` [free tier VM](https://cloud.google.com/free/docs/free-cloud-features#compute) though performance may suffer. If you are performing batch operations, raising the CPU count will help performance. If you have a very large and busy Workspace instance downloading reports or Drive file lists may require more RAM. - * Set Service account under Identity and AOI access - * Choose the service account you created above instead. + * Set `Service account` under `Identity and API access/API and identity management`; choose the service account you created above. + * Select `Set access for each API` + * Enable `Cloud Platform` * GAMADV-XTD3 does not use a significant amount of storage, unless you have specific storage needs the default disk size should suffice. * Leave other VM instance settings at their defaults unless you know what you are doing. - * Click Create + * Click `Create` 5. Install GAMADV-XTD3 on the VM * See: https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Install-Advanced-GAM diff --git a/docs/Version-and-Help.md b/docs/Version-and-Help.md index b213415c..049a6bd0 100644 --- a/docs/Version-and-Help.md +++ b/docs/Version-and-Help.md @@ -3,7 +3,7 @@ Print the current version of Gam with details ``` gam version -GAMADV-XTD3 6.80.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.80.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.4 64-bit final MacOS Sonoma 14.5 x86_64 @@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00 Print the current version of Gam with details and time offset information ``` gam version timeoffset -GAMADV-XTD3 6.80.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.80.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.4 64-bit final MacOS Sonoma 14.5 x86_64 @@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second Print the current version of Gam with extended details and SSL information ``` gam version extended -GAMADV-XTD3 6.80.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.80.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.4 64-bit final MacOS Sonoma 14.5 x86_64 @@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64 Path: /Users/Admin/bin/gamadv-xtd3 Version Check: Current: 5.35.08 - Latest: 6.80.03 + Latest: 6.80.04 echo $? 1 ``` @@ -72,7 +72,7 @@ echo $? Print the current version number without details ``` gam version simple -6.80.03 +6.80.04 ``` In Linux/MacOS you can do: ``` @@ -82,7 +82,7 @@ echo $VER Print the current version of Gam and address of this Wiki ``` gam help -GAM 6.80.03 - https://github.com/taers232c/GAMADV-XTD3 +GAM 6.80.04 - https://github.com/taers232c/GAMADV-XTD3 Ross Scroggs Python 3.12.4 64-bit final MacOS Sonoma 14.5 x86_64 diff --git a/src/GamUpdate.txt b/src/GamUpdate.txt index d8ef7758..ee985008 100644 --- a/src/GamUpdate.txt +++ b/src/GamUpdate.txt @@ -2,6 +2,10 @@ Merged GAM-Team version +6.80.04 + +Cleaned up progress messages in `gam create|update course ... copyfrom`. + 6.80.03 Added option `stripcrsfromname` to `gam print driveactivity` that causes carriage returns, diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 1723b5b8..a9564de8 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -45410,6 +45410,13 @@ class CourseAttributes(): 'updateTime', ] + MAX_TITLE_DISPLAY_LENGTH = 34 + + def trimTitle(self, title): + if len(title) <= self.MAX_TITLE_DISPLAY_LENGTH: + return title + return title[:self.MAX_TITLE_DISPLAY_LENGTH]+'...' + def CleanMaterials(self, body, entityType, entityId): if 'materials' not in body: return @@ -45438,7 +45445,7 @@ class CourseAttributes(): action = Act.Get() Act.Set(Act.COPY) entityActionNotPerformedWarning([Ent.COURSE, self.courseId, entityType, entityId, - Ent.COURSE_MATERIAL_FORM, material['form'].get('title', UNKNOWN)], + Ent.COURSE_MATERIAL_FORM, self.trimTitle(material['form'].get('title', UNKNOWN))], Msg.NOT_COPYABLE) Act.Set(action) @@ -45677,6 +45684,10 @@ class CourseAttributes(): pass return False + def getItemIdTitle(self, body): + id = body.pop('id') + return self.trimTitle(body.get('title', id)) + def checkItemCopyable(self, state, newCourseId, entityType, entityId, body, individualStudentOption, clarg, j, jcount): if state == 'DELETED': entityModifierItemValueListActionNotPerformedWarning([Ent.COURSE, newCourseId, entityType, entityId], Act.MODIFIER_FROM, @@ -45759,12 +45770,12 @@ class CourseAttributes(): for courseAnnouncement in self.courseAnnouncements: j += 1 body = courseAnnouncement.copy() - courseAnnouncementId = body.pop('id') - if not self.checkItemCopyable(courseAnnouncement['state'], newCourseId, Ent.COURSE_ANNOUNCEMENT_ID, courseAnnouncementId, + courseAnnouncementId = self.getItemIdTitle(body) + if not self.checkItemCopyable(courseAnnouncement['state'], newCourseId, Ent.COURSE_ANNOUNCEMENT, courseAnnouncementId, body, self.individualStudentAnnouncements, 'individualstudentannouncements', j, jcount): continue if self.copyMaterialsFiles: - self.CopyMaterials(tdrive, newCourseId, body, Ent.COURSE_ANNOUNCEMENT_ID, courseAnnouncementId, teacherFolderId) + self.CopyMaterials(tdrive, newCourseId, body, Ent.COURSE_ANNOUNCEMENT, courseAnnouncementId, teacherFolderId) try: result = callGAPI(self.tcroom.courses().announcements(), 'create', throwReasons=[GAPI.NOT_FOUND, GAPI.PERMISSION_DENIED, GAPI.FORBIDDEN, @@ -45772,26 +45783,26 @@ class CourseAttributes(): retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, courseId=newCourseId, body=body, fields='id') entityModifierItemValueListActionPerformed([Ent.COURSE, newCourseId, Ent.COURSE_ANNOUNCEMENT_ID, result['id']], Act.MODIFIER_FROM, - [Ent.COURSE, self.courseId, Ent.COURSE_ANNOUNCEMENT_ID, courseAnnouncementId], j, jcount) + [Ent.COURSE, self.courseId, Ent.COURSE_ANNOUNCEMENT, courseAnnouncementId], j, jcount) except GAPI.notFound as e: entityActionFailedWarning([Ent.COURSE, newCourseId], str(e), i, count) return except (GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError, GAPI.permissionDenied, GAPI.forbidden, GAPI.serviceNotAvailable) as e: entityModifierItemValueListActionFailedWarning([Ent.COURSE, newCourseId], Act.MODIFIER_FROM, - [Ent.COURSE, self.courseId, Ent.COURSE_ANNOUNCEMENT_ID, courseAnnouncementId], str(e), j, jcount) + [Ent.COURSE, self.courseId, Ent.COURSE_ANNOUNCEMENT, courseAnnouncementId], str(e), j, jcount) if self.courseMaterials: jcount = len(self.courseMaterials) j = 0 for courseMaterial in self.courseMaterials: j += 1 body = courseMaterial.copy() - courseMaterialId = body.pop('id') - if not self.checkItemCopyable(courseMaterial['state'], newCourseId, Ent.COURSE_MATERIAL_ID, courseMaterialId, + courseMaterialId = self.getItemIdTitle(body) + if not self.checkItemCopyable(courseMaterial['state'], newCourseId, Ent.COURSE_MATERIAL, courseMaterialId, body, self.individualStudentMaterials, 'individualstudentmaterials', j, jcount): continue if self.copyMaterialsFiles: - self.CopyMaterials(tdrive, newCourseId, body, Ent.COURSE_MATERIAL_ID, courseMaterialId, teacherFolderId) + self.CopyMaterials(tdrive, newCourseId, body, Ent.COURSE_MATERIAL, courseMaterialId, teacherFolderId) topicId = body.pop('topicId', None) if self.copyTopics: if topicId: @@ -45807,26 +45818,26 @@ class CourseAttributes(): retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, courseId=newCourseId, body=body, fields='id') entityModifierItemValueListActionPerformed([Ent.COURSE, newCourseId, Ent.COURSE_MATERIAL_ID, result['id']], Act.MODIFIER_FROM, - [Ent.COURSE, self.courseId, Ent.COURSE_MATERIAL_ID, courseMaterialId], j, jcount) + [Ent.COURSE, self.courseId, Ent.COURSE_MATERIAL, courseMaterialId], j, jcount) except GAPI.notFound as e: entityActionFailedWarning([Ent.COURSE, newCourseId], str(e), i, count) return except (GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError, GAPI.permissionDenied, GAPI.forbidden, GAPI.serviceNotAvailable) as e: entityModifierItemValueListActionFailedWarning([Ent.COURSE, newCourseId], Act.MODIFIER_FROM, - [Ent.COURSE, self.courseId, Ent.COURSE_MATERIAL_ID, courseMaterialId], str(e), j, jcount) + [Ent.COURSE, self.courseId, Ent.COURSE_MATERIAL, courseMaterialId], str(e), j, jcount) if self.courseWorks: jcount = len(self.courseWorks) j = 0 for courseWork in self.courseWorks: j += 1 body = courseWork.copy() - courseWorkId = body.pop('id') - if not self.checkItemCopyable(courseWork['state'], newCourseId, Ent.COURSE_WORK_ID, courseWorkId, + courseWorkId = self.getItemIdTitle(body) + if not self.checkItemCopyable(courseWork['state'], newCourseId, Ent.COURSE_WORK, courseWorkId, body, self.individualStudentAssignments, 'individualstudentassignments', j, jcount): continue if self.copyMaterialsFiles: - self.CopyMaterials(tdrive, newCourseId, body, Ent.COURSE_WORK_ID, courseWorkId, teacherFolderId) + self.CopyMaterials(tdrive, newCourseId, body, Ent.COURSE_WORK, courseWorkId, teacherFolderId) topicId = body.pop('topicId', None) if self.copyTopics: if topicId: @@ -45847,14 +45858,14 @@ class CourseAttributes(): retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, courseId=newCourseId, body=body, fields='id') entityModifierItemValueListActionPerformed([Ent.COURSE, newCourseId, Ent.COURSE_WORK_ID, result['id']], Act.MODIFIER_FROM, - [Ent.COURSE, self.courseId, Ent.COURSE_WORK, f'{body.get("title", courseWorkId)}'], j, jcount) + [Ent.COURSE, self.courseId, Ent.COURSE_WORK, courseWorkId], j, jcount) except GAPI.notFound as e: entityActionFailedWarning([Ent.COURSE, newCourseId], str(e), i, count) return except (GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.forbidden, GAPI.serviceNotAvailable) as e: entityModifierItemValueListActionFailedWarning([Ent.COURSE, newCourseId], Act.MODIFIER_FROM, - [Ent.COURSE, self.courseId, Ent.COURSE_WORK, f'{body.get("title", courseWorkId)}'], str(e), j, jcount) + [Ent.COURSE, self.courseId, Ent.COURSE_WORK, courseWorkId], str(e), j, jcount) def CopyFromCourse(self, newCourse, i=0, count=0): action = Act.Get() diff --git a/src/gam/gamlib/glentity.py b/src/gam/gamlib/glentity.py index 01f2f240..d6cd8297 100644 --- a/src/gam/gamlib/glentity.py +++ b/src/gam/gamlib/glentity.py @@ -133,10 +133,12 @@ class GamEntity(): COPYFROM_GROUP = 'cfgr' COURSE = 'cour' COURSE_ALIAS = 'coal' + COURSE_ANNOUNCEMENT = 'cann' COURSE_ANNOUNCEMENT_ID = 'caid' COURSE_ANNOUNCEMENT_STATE = 'cast' COURSE_MATERIAL_DRIVEFILE = 'comd' COURSE_MATERIAL_FORM = 'comf' + COURSE_MATERIAL = 'cmtl' COURSE_MATERIAL_ID = 'cmid' COURSE_MATERIAL_STATE = 'cmst' COURSE_NAME = 'cona' @@ -475,10 +477,12 @@ class GamEntity(): COPYFROM_GROUP: ['Copy From Groups', 'CopyFrom Group'], COURSE: ['Courses', 'Course'], COURSE_ALIAS: ['Course Aliases', 'Course Alias'], + COURSE_ANNOUNCEMENT: ['Course Announcements', 'Course Announcement'], COURSE_ANNOUNCEMENT_ID: ['Course Announcement IDs', 'Course Announcement ID'], COURSE_ANNOUNCEMENT_STATE: ['Course Announcement States', 'Course Announcement State'], COURSE_MATERIAL_DRIVEFILE: ['Course Material Drive Files', 'Course Material Drive File'], COURSE_MATERIAL_FORM: ['Course Material Forms', 'Course Material Form'], + COURSE_MATERIAL: ['Course Materials', 'Course Material'], COURSE_MATERIAL_ID: ['Course Material IDs', 'Course Material ID'], COURSE_MATERIAL_STATE: ['Course Material States', 'Course Material State'], COURSE_NAME: ['Course Names', 'Course Name'],