rubric
은 교사가 학생 제출물을 평가할 때 사용할 수 있는 템플릿입니다. Classroom API를 사용하면 교사를 대신하여 이러한 루브릭을 관리하고 학생 제출물의 루브릭 성적을 읽을 수 있습니다.
그림 1. 클래스룸 과제의 샘플 기준표 보기
이 가이드에서는 Rubrics API의 기본 개념과 기능을 설명합니다. 루브릭의 일반 구조와 클래스룸 UI에서 루브릭 채점이 이루어지는 방식을 알아보려면 다음 고객센터 도움말을 참고하세요.
기본 요건
이 가이드에서는 다음 항목이 있다고 가정합니다.
- Python 3.8.6 이상
- pip 패키지 관리 도구
- Google Cloud 프로젝트
- Google Classroom이 사용 설정되어 있고 Google Workspace for Education Plus 라이선스가 할당된 Google Workspace for Education 계정 개발자 데모 계정이 없는 경우 업그레이드된 계정을 요청할 수 있습니다.
- 테스트 학생 계정이 하나 이상 있는 테스트 수업 테스트에 사용할 수 있는 클래스룸 수업이 없는 경우 UI에서 수업을 만들고 테스트 학생을 추가합니다.
데스크톱 애플리케이션의 사용자 인증 정보 승인
최종 사용자로 인증하고 앱에서 사용자 데이터에 액세스하려면 OAuth 2.0 클라이언트 ID를 하나 이상 만들어야 합니다. 클라이언트 ID는 Google OAuth 서버에서 단일 앱을 식별하는 데 사용됩니다. 앱이 여러 플랫폼에서 실행되는 경우 플랫폼별로 별도의 클라이언트 ID를 만들어야 합니다.
- Google Cloud 콘솔에서 Google Cloud 사용자 인증 정보 페이지로 이동합니다.
- 사용자 인증 정보 만들기 > OAuth 클라이언트 ID를 클릭합니다.
- 애플리케이션 유형 > 데스크톱 앱을 클릭합니다.
- 이름 필드에 사용자 인증 정보의 이름을 입력합니다. 이 이름은 Google Cloud 콘솔에만 표시됩니다. 예를 들어 'Rubrics client'입니다.
- 만들기를 클릭합니다. OAuth 클라이언트 생성 화면이 표시되고 새 클라이언트 ID와 클라이언트 보안 비밀번호가 표시됩니다.
- JSON 다운로드를 클릭한 다음 확인을 클릭합니다. 새로 만든 사용자 인증 정보가 OAuth 2.0 클라이언트 ID 아래에 표시됩니다.
- 다운로드한 JSON 파일을
credentials.json
로 저장하고 작업 디렉터리로 파일을 이동합니다. - 사용자 인증 정보 만들기 > API 키를 클릭하고 API 키를 기록합니다.
자세한 내용은 액세스 사용자 인증 정보 만들기를 참고하세요.
OAuth 범위 구성
프로젝트의 기존 OAuth 범위에 따라 추가 범위를 구성해야 할 수도 있습니다.
- OAuth 동의 화면으로 이동합니다.
- 앱 수정 > 저장 후 계속을 클릭하여 범위 화면으로 이동합니다.
- 범위 추가 또는 삭제를 클릭합니다.
- 다음 범위가 아직 없는 경우 추가합니다.
https://www.googleapis.com/auth/classroom.coursework.students
https://www.googleapis.com/auth/classroom.courses
- 그런 다음 업데이트 클릭 > 저장 후 계속 > 저장 후 계속 > 대시보드로 돌아가기를 클릭합니다.
자세한 내용은 OAuth 동의 화면 구성을 참고하세요.
classroom.coursework.students
범위는 루브릭에 대한 읽기 및 쓰기 액세스 권한 (CourseWork
액세스 권한 포함)을 지원하고 classroom.courses
범위는 강의 읽기 및 쓰기를 허용합니다.
특정 메서드에 필요한 범위는 메서드의 참조 문서에 나와 있습니다. courses.courseWork.rubrics.create
승인 범위를 예로 참고하세요. Google API의 OAuth 2.0 범위에서 모든 클래스룸 범위를 확인할 수 있습니다.
샘플 구성
작업 디렉터리에서 Python용 Google 클라이언트 라이브러리를 설치합니다.
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
클라이언트 라이브러리를 빌드하고 YOUR_API_KEY
대신 API 키를 사용하여 사용자를 승인하는 main.py
이라는 파일을 만듭니다.
import json
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/classroom.courses',
'https://www.googleapis.com/auth/classroom.coursework.students']
def build_authenticated_service(api_key):
"""Builds the Classroom service."""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run.
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
# Build the Classroom service.
service = build(
serviceName="classroom",
version="v1",
credentials=creds,
discoveryServiceUrl=f"https://classroom.googleapis.com/$discovery/rest?labels=DEVELOPER_PREVIEW&key={api_key}")
return service
except HttpError as error:
print('An error occurred: %s' % error)
if __name__ == '__main__':
service = build_authenticated_service(YOUR_API_KEY)
python main.py
를 사용하여 스크립트를 실행합니다. 로그인하고 OAuth 범위에 동의하라는 메시지가 표시됩니다.
할당 만들기
루브릭은 과제 또는 CourseWork
와 연결되며 해당 CourseWork
의 컨텍스트에서만 의미가 있습니다. 루브릭은 상위 CourseWork
항목을 만든 Google Cloud 프로젝트에서만 만들 수 있습니다. 이 가이드에서는 스크립트를 사용하여 새 CourseWork
할당을 만듭니다.
다음을 main.py
에 추가합니다.
def get_latest_course(service):
"""Retrieves the last created course."""
try:
response = service.courses().list(pageSize=1).execute()
courses = response.get("courses", [])
if not courses:
print("No courses found. Did you remember to create one in the UI?")
return
course = courses[0]
return course
except HttpError as error:
print(f"An error occurred: {error}")
return error
def create_coursework(service, course_id):
"""Creates and returns a sample coursework."""
try:
coursework = {
"title": "Romeo and Juliet analysis.",
"description": """Write a paper arguing that Romeo and Juliet were
time travelers from the future.""",
"workType": "ASSIGNMENT",
"state": "PUBLISHED",
}
coursework = service.courses().courseWork().create(
courseId=course_id, body=coursework).execute()
return coursework
except HttpError as error:
print(f"An error occurred: {error}")
return error
이제 main.py
를 업데이트하여 방금 만든 테스트 클래스의 course_id
를 가져오고, 새 샘플 과제를 만들고, 과제의 coursework_id
를 가져옵니다.
if __name__ == '__main__':
service = build_authenticated_service(YOUR_API_KEY)
course = get_latest_course(service)
course_id = course.get("id")
course_name = course.get("name")
print(f"'{course_name}' course ID: {course_id}")
coursework = create_coursework(service, course_id)
coursework_id = coursework.get("id")
print(f"Assignment created with ID {coursework_id}")
#TODO(developer): Save the printed course and coursework IDs.
course_id
및 coursework_id
을 저장합니다. 이는 모든 평가 기준 CRUD 작업에 필요합니다.
이제 클래스룸에 샘플 CourseWork
이 표시됩니다.
그림 2. 클래스룸의 샘플 과제 보기
사용자 자격 확인
평가 기준을 만들고 업데이트하려면 요청을 하는 사용자와 해당 과정 소유자 모두에게 Google Workspace for Education Plus 라이선스가 할당되어 있어야 합니다. 클래스룸은 개발자가 사용자가 액세스할 수 있는 기능을 확인할 수 있도록 사용자 자격 엔드포인트를 지원합니다.
main.py
를 업데이트하고 실행하여 테스트 계정에 루브릭 기능에 대한 액세스 권한이 있는지 확인합니다.
if __name__ == '__main__':
service = build_authenticated_service(YOUR_API_KEY)
capability = service.userProfiles().checkUserCapability(
userId='me',
# Specify the preview version. checkUserCapability is
# supported in V1_20240930_PREVIEW and later.
previewVersion="V1_20240930_PREVIEW",
capability="CREATE_RUBRIC").execute()
if not capability.get('allowed'):
print('User ineligible for rubrics creation.')
# TODO(developer): in a production app, this signal could be used to
# proactively hide any rubrics related features from users or encourage
# them to upgrade to the appropriate license.
else:
print('User eligible for rubrics creation.')
기준표 만들기
이제 루브릭을 관리할 준비가 되었습니다.
전체 평가 기준 객체가 포함된 create()
호출을 사용하여 CourseWork
에서 평가 기준을 만들 수 있습니다. 기준 및 수준의 ID 속성은 생략됩니다 (생성 시 생성됨).
main.py
에 다음 함수를 추가합니다.
def create_rubric(service, course_id, coursework_id):
"""Creates an example rubric on a coursework."""
try:
body = {
"criteria": [
{
"title": "Argument",
"description": "How well structured your argument is.",
"levels": [
{"title": "Convincing",
"description": "A compelling case is made.", "points": 30},
{"title": "Passable",
"description": "Missing some evidence.", "points": 20},
{"title": "Needs Work",
"description": "Not enough strong evidence..", "points": 0},
]
},
{
"title": "Spelling",
"description": "How well you spelled all the words.",
"levels": [
{"title": "Perfect",
"description": "No mistakes.", "points": 20},
{"title": "Great",
"description": "A mistake or two.", "points": 15},
{"title": "Needs Work",
"description": "Many mistakes.", "points": 5},
]
},
{
"title": "Grammar",
"description": "How grammatically correct your sentences are.",
"levels": [
{"title": "Perfect",
"description": "No mistakes.", "points": 20},
{"title": "Great",
"description": "A mistake or two.", "points": 15},
{"title": "Needs Work",
"description": "Many mistakes.", "points": 5},
]
},
]
}
rubric = service.courses().courseWork().rubrics().create(
courseId=course_id, courseWorkId=coursework_id, body=body
).execute()
print(f"Rubric created with ID {rubric.get('id')}")
return rubric
except HttpError as error:
print(f"An error occurred: {error}")
return error
그런 다음 main.py
를 업데이트하고 실행하여 이전의 Course
및 CourseWork
ID를 사용하여 예시 루브릭을 만듭니다.
if __name__ == '__main__':
service = build_authenticated_service(YOUR_API_KEY)
capability = service.userProfiles().checkUserCapability(
userId='me',
# Specify the preview version. checkUserCapability is
# supported in V1_20240930_PREVIEW and later.
previewVersion="V1_20240930_PREVIEW",
capability="CREATE_RUBRIC").execute()
if not capability.get('allowed'):
print('User ineligible for rubrics creation.')
# TODO(developer): in a production app, this signal could be used to
# proactively hide any rubrics related features from users or encourage
# them to upgrade to the appropriate license.
else:
rubric = create_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
print(json.dumps(rubric, indent=4))
기준표 표현에 관한 참고사항:
- 기준 및 수준 순서가 클래스룸 UI에 반영됩니다.
- 점수가 매겨진 레벨 (
points
속성이 있는 레벨)은 점수별로 오름차순 또는 내림차순으로 정렬해야 합니다 (무작위로 정렬할 수 없음). - 교사는 UI에서 기준과 점수가 부여된 수준 (점수가 부여되지 않은 수준은 제외)을 다시 정렬할 수 있으며, 이렇게 하면 데이터의 순서가 변경됩니다.
루브릭 구조에 관한 추가 주의사항은 제한사항을 참고하세요.
UI로 돌아가면 과제에 루브릭이 표시됩니다.
그림 3. 클래스룸 과제의 샘플 기준표 보기
기준표 읽기
루브릭은 표준 list()
및 get()
메서드로 읽을 수 있습니다.
과제에는 최대 하나의 루브릭만 있을 수 있으므로 list()
이 직관적이지 않은 것처럼 보일 수 있지만 아직 루브릭 ID가 없는 경우 유용합니다. CourseWork
와 연결된 루브릭이 없으면 list()
응답이 비어 있습니다.
main.py
에 다음 함수를 추가합니다.
def get_rubric(service, course_id, coursework_id):
"""
Get the rubric on a coursework. There can only be at most one.
Returns null if there is no rubric.
"""
try:
response = service.courses().courseWork().rubrics().list(
courseId=course_id, courseWorkId=coursework_id
).execute()
rubrics = response.get("rubrics", [])
if not rubrics:
print("No rubric found for this assignment.")
return
rubric = rubrics[0]
return rubric
except HttpError as error:
print(f"An error occurred: {error}")
return error
main.py
를 업데이트하고 실행하여 추가한 기준표를 가져옵니다.
if __name__ == '__main__':
service = build_authenticated_service(YOUR_API_KEY)
rubric = get_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
print(json.dumps(rubric, indent=4))
#TODO(developer): Save the printed rubric ID.
나중에 사용할 수 있도록 루브릭의 id
속성을 기록해 둡니다.
Get()
는 루브릭 ID가 있는 경우에 잘 작동합니다. 함수에서 get()
를 대신 사용하면 다음과 같이 표시될 수 있습니다.
def get_rubric(service, course_id, coursework_id, rubric_id):
"""
Get the rubric on a coursework. There can only be at most one.
Returns a 404 if there is no rubric.
"""
try:
rubric = service.courses().courseWork().rubrics().get(
courseId=course_id,
courseWorkId=coursework_id,
id=rubric_id
).execute()
return rubric
except HttpError as error:
print(f"An error occurred: {error}")
return error
이 구현은 루브릭이 없으면 404를 반환합니다.
기준표 업데이트
루브릭 업데이트는 patch()
호출을 통해 이루어집니다. 평가 기준의 구조가 복잡하므로 전체 criteria
속성이 대체되는 읽기-수정-쓰기 패턴으로 업데이트해야 합니다.
업데이트 규칙은 다음과 같습니다.
- ID 없이 추가된 기준 또는 수준은 추가로 간주됩니다.
- 이전에 있던 기준이나 등급이 누락된 경우 삭제된 것으로 간주됩니다.
- 기존 ID가 있지만 데이터가 수정된 기준 또는 수준은 수정사항으로 간주됩니다. 수정되지 않은 속성은 그대로 유지됩니다.
- 새 ID 또는 알 수 없는 ID와 함께 제공된 기준 또는 수준은 오류로 간주됩니다.
- 새 기준과 수준의 순서는 새 UI 순서로 간주됩니다(앞서 언급한 제한사항 포함).
루브릭을 업데이트하는 함수를 추가합니다.
def update_rubric(service, course_id, coursework_id, rubric_id, body):
"""
Updates the rubric on a coursework.
"""
try:
rubric = service.courses().courseWork().rubrics().patch(
courseId=course_id,
courseWorkId=coursework_id,
id=rubric_id,
body=body,
updateMask='criteria'
).execute()
return rubric
except HttpError as error:
print(f"An error occurred: {error}")
return error
이 예에서는 criteria
필드가 updateMask
로 수정되도록 지정됩니다.
그런 다음 앞서 언급한 업데이트 규칙 각각에 맞게 변경하도록 main.py
를 수정합니다.
if __name__ == '__main__':
service = build_authenticated_service(YOUR_API_KEY)
capability = service.userProfiles().checkUserCapability(
userId='me',
# Specify the preview version. checkUserCapability is
# supported in V1_20240930_PREVIEW and later.
previewVersion="V1_20240930_PREVIEW",
capability="CREATE_RUBRIC").execute()
if not capability.get('allowed'):
print('User ineligible for rubrics creation.')
# TODO(developer): in a production app, this signal could be used to
# proactively hide any rubrics related features from users or encourage
# them to upgrade to the appropriate license.
else:
# Get the latest rubric.
rubric = get_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
criteria = rubric.get("criteria")
"""
The "criteria" property should look like this:
[
{
"id": "NkEyMdMyMzM2Nxkw",
"title": "Argument",
"description": "How well structured your argument is.",
"levels": [
{
"id": "NkEyMdMyMzM2Nxkx",
"title": "Convincing",
"description": "A compelling case is made.",
"points": 30
},
{
"id": "NkEyMdMyMzM2Nxky",
"title": "Passable",
"description": "Missing some evidence.",
"points": 20
},
{
"id": "NkEyMdMyMzM2Nxkz",
"title": "Needs Work",
"description": "Not enough strong evidence..",
"points": 0
}
]
},
{
"id": "NkEyMdMyMzM2Nxk0",
"title": "Spelling",
"description": "How well you spelled all the words.",
"levels": [...]
},
{
"id": "NkEyMdMyMzM2Nxk4",
"title": "Grammar",
"description": "How grammatically correct your sentences are.",
"levels": [...]
}
]
"""
# Make edits. This example will make one of each type of change.
# Add a new level to the first criteria. Levels must remain sorted by
# points.
new_level = {
"title": "Profound",
"description": "Truly unique insight.",
"points": 50
}
criteria[0]["levels"].insert(0, new_level)
# Remove the last criteria.
del criteria[-1]
# Update the criteria titles with numeric prefixes.
for index, criterion in enumerate(criteria):
criterion["title"] = f"{index}: {criterion['title']}"
# Resort the levels from descending to ascending points.
for criterion in criteria:
criterion["levels"].sort(key=lambda level: level["points"])
# Update the rubric with a patch call.
new_rubric = update_rubric(
service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID, YOUR_RUBRIC_ID, rubric)
print(json.dumps(new_rubric, indent=4))
이제 변경사항이 클래스룸의 교사에게 반영됩니다.
그림 4. 업데이트된 루브릭의 보기
기준표로 평가된 제출물 보기
현재 API에서는 학생 제출물을 기준표로 채점할 수 없지만 클래스룸 UI에서 기준표로 채점된 제출물의 기준표 성적을 읽을 수 있습니다.
클래스룸 UI에서 학생으로 로그인하여 샘플 과제를 완료하고 제출합니다. 그런 다음 교사가 루브릭을 사용하여 과제를 수동으로 채점합니다.
그림 5. 채점 중인 기준표의 교사 보기
StudentSubmissions
에 루브릭으로 채점된 경우 초안 및 할당된 채점 상태에서 교사가 선택한 점수와 수준을 각각 나타내는 draftRubricGrades
및 assignedRubricGrades
이라는 두 가지 새로운 속성이 있습니다.
기존 studentSubmissions.get()
및 studentSubmissions.list()
메서드를 사용하여 성적이 부여된 제출물을 볼 수 있습니다.
학생 제출 과제를 나열하는 다음 함수를 main.py
에 추가합니다.
def get_latest_submission(service, course_id, coursework_id):
"""Retrieves the last submission for an assignment."""
try:
response = service.courses().courseWork().studentSubmissions().list(
courseId = course_id,
courseWorkId = coursework_id,
pageSize=1
).execute()
submissions = response.get("studentSubmissions", [])
if not submissions:
print(
"""No submissions found. Did you remember to turn in and grade
the assignment in the UI?""")
return
submission = submissions[0]
return submission
except HttpError as error:
print(f"An error occurred: {error}")
return error
그런 다음 main.py
를 업데이트하고 실행하여 제출 성적을 확인합니다.
if __name__ == '__main__':
service = build_authenticated_service(YOUR_API_KEY)
submission = get_latest_submission(
service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
print(json.dumps(submission, indent=4))
draftRubricGrades
및 assignedRubricGrades
에는 다음이 포함됩니다.
- 해당 기준표 기준의
criterionId
입니다. points
교사가 각 기준에 할당한 점수입니다. 선택한 학년에서 가져온 것일 수도 있지만 교사가 이를 덮어썼을 수도 있습니다.- 각 기준에 대해 선택된 수준의
levelId
입니다. 교사가 수준을 선택하지 않았지만 기준에 점수를 할당한 경우 이 필드는 표시되지 않습니다.
이 목록에는 교사가 수준을 선택하거나 점수를 설정한 기준의 항목만 포함됩니다. 예를 들어 교사가 평가 중에 하나의 기준만 상호작용하도록 선택하면 루브릭에 기준이 많더라도 draftRubricGrades
및 assignedRubricGrades
에는 항목이 하나만 있습니다.
기준표 삭제
표준 delete()
요청을 사용하여 루브릭을 삭제할 수 있습니다. 다음 코드는 완전성을 위한 함수 예를 보여주지만, 이미 채점이 시작되었으므로 현재 루브릭을 삭제할 수 없습니다.
def delete_rubric(service, course_id, coursework_id, rubric_id):
"""Deletes the rubric on a coursework."""
try:
service.courses().courseWork().rubrics().delete(
courseId=course_id,
courseWorkId=coursework_id,
id=rubric_id
).execute()
except HttpError as error:
print(f"An error occurred: {error}")
return error
루브릭 내보내기 및 가져오기
기준표는 교사가 재사용할 수 있도록 Google Sheets로 수동으로 내보낼 수 있습니다.
코드에서 루브릭 기준을 지정하는 것 외에도 criteria
대신 루브릭 본문에 sourceSpreadsheetId
를 지정하여 내보낸 시트에서 루브릭을 만들고 업데이트할 수 있습니다.
def create_rubric_from_sheet(service, course_id, coursework_id, sheet_id):
"""Creates an example rubric on a coursework."""
try:
body = {
"sourceSpreadsheetId": sheet_id
}
rubric = service.courses().courseWork().rubrics().create(
courseId=course_id, courseWorkId=coursework_id, body=body
).execute()
print(f"Rubric created with ID {rubric.get('id')}")
return rubric
except HttpError as error:
print(f"An error occurred: {error}")
return error