這是 Classroom 外掛程式操作說明系列的第四篇操作說明。
在本逐步操作說明中,您將與 Google Classroom API 互動,建立附件。您必須提供路徑,讓使用者查看附件內容。畫面會因使用者在課程中的角色而有所不同。本逐步操作說明涵蓋內容類型附件,這類附件不需要學生提交。
在本逐步操作說明中,您將完成下列操作:
- 擷取並使用下列外掛程式查詢參數:
addOnToken
:傳遞至附件探索檢視畫面的授權權杖。itemId
:接收外掛程式附件的 CourseWork、CourseWorkMaterial 或 Announcement 專屬 ID。itemType
:可選填「courseWork」、「courseWorkMaterials」或「announcement」。courseId
:建立作業的 Google Classroom 課程專屬 ID。attachmentId
:Google Classroom 在建立後指派給外掛程式附件的專屬 ID。
- 為內容類型附件實作永久儲存空間。
- 提供路徑來建立附件,並提供老師檢視畫面和學生檢視畫面的 iframe。
- 向 Google Classroom 外掛程式 API 發出以下要求:
- 建立新的附件。
- 取得外掛程式內容,用於判斷登入使用者是學生還是老師。
完成後,您可以以教師身分登入 Google Classroom 使用者介面,為作業建立內容類型附件。課程中的老師和學生也可以查看內容。
啟用 Classroom API
從這個步驟開始呼叫 Classroom API。您必須先為 Google Cloud 專案啟用 API,才能呼叫該 API。前往 Google Classroom API 程式庫項目,然後選擇「啟用」。
處理附件探索檢視畫面的查詢參數
如先前所述,Google Classroom 會在 iframe 中載入附件探索檢視畫面時傳遞查詢參數:
courseId
:目前 Classroom 課程的 ID。itemId
:接收外掛程式附件的 CourseWork、CourseWorkMaterial 或 Announcement 專屬 ID。itemType
:"courseWork"、"courseWorkMaterials" 或 "announcement"。addOnToken
:用於授權特定 Classroom 外掛動作的權杖。login_hint
:目前使用者的 Google ID。
本逐步操作說明將說明 courseId
、itemId
、itemType
和 addOnToken
。在向 Classroom API 發出呼叫時保留並傳遞這些值。
如同上一個逐步操作步驟,請將傳入的查詢參數值儲存在工作階段中。我們必須在首次開啟附件探索檢視畫面時執行這項操作,因為這是 Classroom 傳遞這些查詢參數的唯一機會。
Python
前往提供附件探索檢視畫面路徑的 Flask 伺服器檔案 (如果您按照我們的範例操作,則為 attachment-discovery-routes.py
)。在外掛程式到達路徑 (在本例中為 /classroom-addon
) 的頂端,擷取並儲存 courseId
、itemId
、itemType
和 addOnToken
查詢參數。
# Retrieve the itemId, courseId, and addOnToken query parameters.
if flask.request.args.get("itemId"):
flask.session["itemId"] = flask.request.args.get("itemId")
if flask.request.args.get("itemType"):
flask.session["itemType"] = flask.request.args.get("itemType")
if flask.request.args.get("courseId"):
flask.session["courseId"] = flask.request.args.get("courseId")
if flask.request.args.get("addOnToken"):
flask.session["addOnToken"] = flask.request.args.get("addOnToken")
只有在這些值存在時,才將這些值寫入工作階段;如果使用者稍後在未關閉 iframe 的情況下返回附件探索檢視畫面,系統就不會再次傳遞這些值。
為內容類型附件新增永久儲存空間
您需要建立附件的本機記錄。這樣一來,您就能使用 Classroom 提供的 ID 查詢老師選取的內容。
為 Attachment
設定資料庫結構定義。我們提供的範例會顯示圖片和說明文字的附件。Attachment
包含下列屬性:
attachment_id
:附件的專屬 ID。由 Classroom 指派,並在建立附件時傳回在回應中。image_filename
:要顯示的圖片本機檔案名稱。image_caption
:與圖片一併顯示的說明文字。
Python
擴充先前步驟中的 SQLite 和 flask_sqlalchemy
實作項目。
前往您定義 User 資料表的檔案 (如果您是按照我們的範例操作,請參閱 models.py
)。在 User
類別下方檔案的底部新增以下內容。
class Attachment(db.Model):
# The attachmentId is the unique identifier for the attachment.
attachment_id = db.Column(db.String(120), primary_key=True)
# The image filename to store.
image_filename = db.Column(db.String(120))
# The image caption to store.
image_caption = db.Column(db.String(120))
將新的附件類別匯入至伺服器檔案,並加入附件處理路徑。
設定新路徑
請在應用程式中設定一些新頁面,開始本逐步操作步驟。使用者可透過這個功能,透過我們的外掛程式建立及查看內容。
新增附件建立路徑
您需要提供網頁,讓老師選取內容,並提出建立附件的請求。實作 /attachment-options
路徑,以便顯示教師可選取的內容選項。您也需要內容選取和建立確認頁面的範本。我們提供的範例包含這些範本,也可以顯示 Classroom API 的請求和回應。
請注意,您可以改為修改現有的附件探索檢視畫面到達網頁,以顯示內容選項,而非建立新的 /attachment-options
頁面。建議您為本練習建立新頁面,以便保留第二個逐步操作步驟中實作的 SSO 行為,例如撤銷應用程式權限。這些資訊應可在建構及測試外掛程式時派上用場。
老師可以從我們提供的範例中,選取一小組附註圖片。我們提供四張知名地標的圖片,說明文字則是取自檔案名稱。
Python
在我們提供的範例中,這項資訊位於 webapp/attachment_routes.py
檔案中。
@app.route("/attachment-options", methods=["GET", "POST"])
def attachment_options():
"""
Render the attachment options page from the "attachment-options.html"
template.
This page displays a grid of images that the user can select using
checkboxes.
"""
# A list of the filenames in the static/images directory.
image_filenames = os.listdir(os.path.join(app.static_folder, "images"))
# The image_list_form_builder method creates a form that displays a grid
# of images, checkboxes, and captions with a Submit button. All images
# passed in image_filenames will be shown, and the captions will be the
# title-cased filenames.
# The form must be built dynamically due to limitations in WTForms. The
# image_list_form_builder method therefore also returns a list of
# attribute names in the form, which will be used by the HTML template
# to properly render the form.
form, var_names = image_list_form_builder(image_filenames)
# If the form was submitted, validate the input and create the attachments.
if form.validate_on_submit():
# Build a dictionary that maps image filenames to captions.
# There will be one dictionary entry per selected item in the form.
filename_caption_pairs = construct_filename_caption_dictionary_list(
form)
# Check that the user selected at least one image, then proceed to
# make requests to the Classroom API.
if len(filename_caption_pairs) > 0:
return create_attachments(filename_caption_pairs)
else:
return flask.render_template(
"create-attachment.html",
message="You didn't select any images.",
form=form,
var_names=var_names)
return flask.render_template(
"attachment-options.html",
message=("You've reached the attachment options page. "
"Select one or more images and click 'Create Attachment'."),
form=form,
var_names=var_names,
)
這會產生類似下方的「建立附件」頁面:
老師可以選取多張圖片。為老師在 create_attachments
方法中選取的每張圖片建立一個附件。
建立附件建立要求
您現在已瞭解老師想附加哪些內容,請向 Classroom API 發出要求,在作業中建立附件。收到 Classroom API 的回應後,將附件詳細資料儲存在資料庫中。
首先取得 Classroom 服務的例項:
Python
在我們提供的範例中,這項資訊位於 webapp/attachment_routes.py
檔案中。
def create_attachments(filename_caption_pairs):
"""
Create attachments and show an acknowledgement page.
Args:
filename_caption_pairs: A dictionary that maps image filenames to
captions.
"""
# Get the Google Classroom service.
classroom_service = googleapiclient.discovery.build(
serviceName="classroom",
version="v1",
credentials=credentials)
向 courses.courseWork.addOnAttachments
端點發出 CREATE
要求。針對老師選取的每張圖片,首先建構 AddOnAttachment
物件:
Python
在我們提供的範例中,這是 create_attachments
方法的後續動作。
# Create a new attachment for each image that was selected.
attachment_count = 0
for key, value in filename_caption_pairs.items():
attachment_count += 1
# Create a dictionary with values for the AddOnAttachment object fields.
attachment = {
# Specifies the route for a teacher user.
"teacherViewUri": {
"uri":
flask.url_for(
"load_content_attachment", _scheme='https', _external=True),
},
# Specifies the route for a student user.
"studentViewUri": {
"uri":
flask.url_for(
"load_content_attachment", _scheme='https', _external=True)
},
# The title of the attachment.
"title": f"Attachment {attachment_count}",
}
每個附件至少必須提供 teacherViewUri
、studentViewUri
和 title
欄位。teacherViewUri
和 studentViewUri
代表在相應使用者類型開啟附件時載入的網址。
將要求主體中的 AddOnAttachment
物件傳送至適當的 addOnAttachments
端點。請在每項要求中提供 courseId
、itemId
、itemType
和 addOnToken
ID。
Python
在我們提供的範例中,這是 create_attachments
方法的後續動作。
# Use the itemType to determine which stream item type the teacher created
match flask.session["itemType"]:
case "announcements":
parent = classroom_service.courses().announcements()
case "courseWorkMaterials":
parent = classroom_service.courses().courseWorkMaterials()
case _:
parent = classroom_service.courses().courseWork()
# Issue a request to create the attachment.
resp = parent.addOnAttachments().create(
courseId=flask.session["courseId"],
itemId=flask.session["itemId"],
addOnToken=flask.session["addOnToken"],
body=attachment).execute()
在本機資料庫中建立此附件的項目,以便日後載入正確的內容。Classroom 會在建立要求的回應中傳回唯一的 id
值,因此請將其用作資料庫中的主索引鍵。請注意,Classroom 在開啟老師和學生檢視畫面時,會傳遞 attachmentId
查詢參數:
Python
在我們提供的範例中,這是 create_attachments
方法的後續動作。
# Store the value by id.
new_attachment = Attachment(
# The new attachment's unique ID, returned in the CREATE response.
attachment_id=resp.get("id"),
image_filename=key,
image_caption=value)
db.session.add(new_attachment)
db.session.commit()
此時,您可以考慮將使用者導向確認頁面,確認他們已成功建立附件。
允許外掛程式中的附件
您可以在此時將任何適當的地址新增至 Google Workspace Marketplace SDK 應用程式設定頁面中的「允許的附件 URI 前置字串」欄位。您的外掛程式只能使用此頁面所列的其中一個 URI 前置字串建立附件。這是一項安全措施,有助於降低中間人攻擊的可能性。
最簡單的方法是在這個欄位中提供頂層網域,例如 https://example.com
。如果您使用本機電腦做為網路伺服器,https://localhost:<your port number>/
就會運作。
為老師和學生檢視畫面新增路徑
有四個 iframe 可能會載入 Google Classroom 外掛程式。您目前只建立了用於放送附件探索檢視畫面 iframe 的路徑。接著,請新增路徑來提供教師和學生檢視畫面 iframe。
教師模式 iframe 是顯示學生體驗預覽畫面的必要元素,但可視需要納入其他資訊或編輯功能。
學生檢視畫面是指學生開啟外掛程式附件時,系統會向他們顯示的頁面。
為了完成本練習,請建立單一 /load-content-attachment
路由,同時支援老師和學生檢視畫面。使用 Classroom API 方法,判斷使用者在網頁載入時是老師或學生。
Python
在我們提供的範例中,這項資訊位於 webapp/attachment_routes.py
檔案中。
@app.route("/load-content-attachment")
def load_content_attachment():
"""
Load the attachment for the user's role."""
# Since this is a landing page for the Teacher and Student View iframes, we
# need to preserve the incoming query parameters.
if flask.request.args.get("itemId"):
flask.session["itemId"] = flask.request.args.get("itemId")
if flask.request.args.get("itemType"):
flask.session["itemType"] = flask.request.args.get("itemType")
if flask.request.args.get("courseId"):
flask.session["courseId"] = flask.request.args.get("courseId")
if flask.request.args.get("attachmentId"):
flask.session["attachmentId"] = flask.request.args.get("attachmentId")
請注意,您也應在此時驗證使用者。您也應在此處處理 login_hint
查詢參數,並視需要將使用者導向授權流程。如要進一步瞭解這個流程,請參閱先前逐步操作說明中討論的登入指引詳細資料。
然後將要求傳送至與項目類型相符的 getAddOnContext
端點。
Python
在我們提供的範例中,這是 load_content_attachment
方法的延續。
# Create an instance of the Classroom service.
classroom_service = googleapiclient.discovery.build(
serviceName="classroom"
version="v1",
credentials=credentials)
# Use the itemType to determine which stream item type the teacher created
match flask.session["itemType"]:
case "announcements":
parent = classroom_service.courses().announcements()
case "courseWorkMaterials":
parent = classroom_service.courses().courseWorkMaterials()
case _:
parent = classroom_service.courses().courseWork()
addon_context_response = parent.getAddOnContext(
courseId=flask.session["courseId"],
itemId=flask.session["itemId"]).execute()
這個方法會傳回目前使用者在課程中的角色資訊。視使用者角色而定,變更向使用者顯示的檢視畫面。回應物件中會填入 studentContext
或 teacherContext
欄位之一。請檢查這些資訊,判斷如何回應使用者。
無論如何,請使用 attachmentId
查詢參數值,瞭解要從資料庫中擷取哪個附件。開啟老師或學生檢視畫面 URI 時,系統會提供這個查詢參數。
Python
在我們提供的範例中,這是 load_content_attachment
方法的延續。
# Determine which view we are in by testing the returned context type.
user_context = "student" if addon_context_response.get(
"studentContext") else "teacher"
# Look up the attachment in the database.
attachment = Attachment.query.get(flask.session["attachmentId"])
# Set the text for the next page depending on the user's role.
message_str = f"I see that you're a {user_context}! "
message_str += (
f"I've loaded the attachment with ID {attachment.attachment_id}. "
if user_context == "teacher" else
"Please enjoy this image of a famous landmark!")
# Show the content with the customized message text.
return flask.render_template(
"show-content-attachment.html",
message=message_str,
image_filename=attachment.image_filename,
image_caption=attachment.image_caption,
responses=response_strings)
測試外掛程式
如要測試附件建立作業,請完成下列步驟:
- 以老師測試使用者身分登入 [Google Classroom]。
- 前往「課堂作業」分頁,然後建立新的「作業」。
- 按一下文字區域下方的「外掛程式」按鈕,然後選取所需外掛程式。iframe 會開啟,外掛程式會載入您在 Google Workspace Marketplace SDK 的「App Configuration」頁面中指定的附件設定 URI。
- 選擇要附加至作業的內容。
- 在附件建立流程完成後關閉 iframe。
您應該會在 Google Classroom 的作業建立 UI 中看到附件資訊卡。點選資訊卡開啟「Teacher View」iframe,確認是否顯示正確的附件。按一下「指派」按鈕。
如要測試學生體驗,請完成下列步驟:
- 接著,以學生測試使用者的身分登入 Classroom,並與老師測試使用者在同一門課中。
- 在「課堂作業」分頁中找出測驗作業。
- 展開作業並點選附件資訊卡,即可開啟學生檢視畫面 iframe。
確認學生是否收到正確的附件。
恭喜!您已準備好進行下一個步驟:建立活動類型附件。