수업 과제 및 성적 관리

클래스룸 UI는 과제, 퀴즈 과제, 단답형 질문, 객관식 질문, 자료 등 5가지 유형의 수업 과제를 지원합니다. Classroom API는 과제, 단답형 질문, 객관식 질문 등 현재 세 가지 유형을 지원하며 API에서는 CourseWorkType라고 합니다.

Materials 연결을 지원합니다.

이 기능에 액세스하려면 마감일 또는 최고 점수와 같은 추가 자료 및 세부정보를 포함하여 특정 과정에서 학생에게 할당된 과제 또는 질문을 나타내는 CourseWork 리소스를 사용하면 됩니다.

CourseWork 리소스 외에도 StudentSubmission 리소스를 사용해 완료된 과제를 관리할 수 있습니다. 다음 섹션에서 이를 더 자세히 설명합니다.

과제 만들기

과제는 강의의 교사를 대신해서만 만들 수 있으며, 학생을 대신하여 강의에서 과제를 만들려고 하면 403 PERMISSION_DENIED 오류가 발생합니다. 마찬가지로 도메인 관리자는 자신이 가르치지 않는 과정에 과제를 만들 수 없으며 API를 통해 할당하려고 하면 403 PERMISSION_DENIED 오류가 발생합니다.

courses.courseWork.create 메서드를 사용하여 할당을 만들 때 아래 샘플 코드와 같이 링크를 materials로 연결할 수 있습니다.

Java

classroom/snippets/src/main/java/CreateCourseWork.java
CourseWork courseWork = null;
try {
  // Create a link to add as a material on course work.
  Link articleLink =
      new Link()
          .setTitle("SR-71 Blackbird")
          .setUrl("https://www.lockheedmartin.com/en-us/news/features/history/blackbird.html");

  // Create a list of Materials to add to course work.
  List<Material> materials = Arrays.asList(new Material().setLink(articleLink));

  /* Create new CourseWork object with the material attached.
  Set workType to `ASSIGNMENT`. Possible values of workType can be found here:
  https://developers.google.com/classroom/reference/rest/v1/CourseWorkType
  Set state to `PUBLISHED`. Possible values of state can be found here:
  https://developers.google.com/classroom/reference/rest/v1/courses.courseWork#courseworkstate */
  CourseWork content =
      new CourseWork()
          .setTitle("Supersonic aviation")
          .setDescription(
              "Read about how the SR-71 Blackbird, the world’s fastest and "
                  + "highest-flying manned aircraft, was built.")
          .setMaterials(materials)
          .setWorkType("ASSIGNMENT")
          .setState("PUBLISHED");

  courseWork = service.courses().courseWork().create(courseId, content).execute();

  /* Prints the created courseWork. */
  System.out.printf("CourseWork created: %s\n", courseWork.getTitle());
} catch (GoogleJsonResponseException e) {
  // TODO (developer) - handle error appropriately
  GoogleJsonError error = e.getDetails();
  if (error.getCode() == 404) {
    System.out.printf("The courseId does not exist: %s.\n", courseId);
  } else {
    throw e;
  }
  throw e;
} catch (Exception e) {
  throw e;
}
return courseWork;

Python

classroom/snippets/classroom_create_coursework.py
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def classroom_create_coursework(course_id):
  """
  Creates the coursework the user has access to.
  Load pre-authorized user credentials from the environment.
  TODO(developer) - See https://developers.google.com/identity
  for guides on implementing OAuth2 for the application.
  """

  creds, _ = google.auth.default()
  # pylint: disable=maybe-no-member

  try:
    service = build("classroom", "v1", credentials=creds)
    coursework = {
        "title": "Ant colonies",
        "description": """Read the article about ant colonies
                              and complete the quiz.""",
        "materials": [
            {"link": {"url": "http://example.com/ant-colonies"}},
            {"link": {"url": "http://example.com/ant-quiz"}},
        ],
        "workType": "ASSIGNMENT",
        "state": "PUBLISHED",
    }
    coursework = (
        service.courses()
        .courseWork()
        .create(courseId=course_id, body=coursework)
        .execute()
    )
    print(f"Assignment created with ID {coursework.get('id')}")
    return coursework

  except HttpError as error:
    print(f"An error occurred: {error}")
    return error


if __name__ == "__main__":
  # Put the course_id of course whose coursework needs to be created,
  # the user has access to.
  classroom_create_coursework(453686957652)

결과에는 다른 API 요청에서 할당을 참조하는 데 사용할 수 있는 서버 할당 식별자가 포함됩니다.

Classroom API를 통해 만든 과제에 링크된 자료를 포함하려면 Link 리소스를 사용하여 대상 URL을 지정합니다. 클래스룸에서 제목과 썸네일 이미지를 자동으로 가져옵니다. 또한 Classroom API는 Google 드라이브 및 YouTube 자료를 기본적으로 지원하며, 자료는 유사한 방식으로 DriveFile 리소스 또는 YouTubeVideo 리소스와 함께 포함할 수 있습니다.

마감일을 지정하려면 dueDatedueTime 필드를 해당하는 UTC 시간으로 설정합니다. 마감일은 미래여야 합니다.

과제 및 질문 가져오기

해당 과정의 학생과 교사 또는 도메인 관리자가 작성한 과제와 질문을 가져올 수 있습니다. 특정 과제 또는 질문을 검색하려면 Course.courseWork.get을 사용하세요. 모든 과제 또는 질문을 검색하려면 (선택적으로 일부 기준과 일치)courses.courseWork.list를 사용하세요.

필요한 범위는 과정에서 요청하는 사용자의 역할에 따라 다릅니다. 사용자가 학생인 경우 다음 범위 중 하나를 사용합니다.

  • https://www.googleapis.com/auth/classroom.coursework.me.readonly
  • https://www.googleapis.com/auth/classroom.coursework.me

사용자가 교사 또는 도메인 관리자인 경우 다음 범위 중 하나를 사용합니다.

  • https://www.googleapis.com/auth/classroom.coursework.students.readonly
  • https://www.googleapis.com/auth/classroom.coursework.students

과제 또는 질문을 검색할 수 있는 권한이 있다고 해서 자료 또는 자료 메타데이터에 액세스하는 권한을 의미하는 것은 아닙니다. 실제로 관리자가 이 과정의 멤버가 아닌 경우 첨부된 드라이브 파일의 제목이 표시되지 않을 수 있습니다. 관리자가 사용자 파일에 액세스하도록 허용하려면 도메인 전체 위임 가이드를 참조하세요.

학생 응답 관리

StudentSubmission 리소스는 과제 또는 질문에 대한 학생의 완료 과제와 성적을 나타냅니다. 새로운 질문이나 과제가 만들어지면 각 학생에 대해 StudentSubmission 리소스가 암시적으로 생성됩니다.

다음 섹션에서는 학생 응답을 관리하는 일반적인 작업을 설명합니다.

학생 응답 가져오기

학생은 자신의 제출물을 검색하고, 교사는 과정에 있는 모든 학생의 제출물을 검색할 수 있으며, 도메인 관리자는 도메인에 있는 모든 학생의 모든 제출물을 검색할 수 있습니다. 학생 제출물마다 식별자가 할당됩니다. 식별자를 알고 있으면 courses.courseWork.studentSubmissions.get를 사용하여 식별자를 검색하세요.

다음 샘플과 같이 courses.courseWork.studentSubmissions.list 메서드를 사용하여 일부 기준과 일치하는 StudentSubmission 리소스를 가져옵니다.

Java

classroom/snippets/src/main/java/ListSubmissions.java
List<StudentSubmission> studentSubmissions = new ArrayList<>();
String pageToken = null;

try {
  do {
    ListStudentSubmissionsResponse response =
        service
            .courses()
            .courseWork()
            .studentSubmissions()
            .list(courseId, courseWorkId)
            .setPageToken(pageToken)
            .execute();

    /* Ensure that the response is not null before retrieving data from it to avoid errors. */
    if (response.getStudentSubmissions() != null) {
      studentSubmissions.addAll(response.getStudentSubmissions());
      pageToken = response.getNextPageToken();
    }
  } while (pageToken != null);

  if (studentSubmissions.isEmpty()) {
    System.out.println("No student submission found.");
  } else {
    for (StudentSubmission submission : studentSubmissions) {
      System.out.printf(
          "Student id (%s), student submission id (%s)\n",
          submission.getUserId(), submission.getId());
    }
  }
} catch (GoogleJsonResponseException e) {
  // TODO (developer) - handle error appropriately
  GoogleJsonError error = e.getDetails();
  if (error.getCode() == 404) {
    System.out.printf(
        "The courseId (%s) or courseWorkId (%s) does not exist.\n", courseId, courseWorkId);
  } else {
    throw e;
  }
} catch (Exception e) {
  throw e;
}
return studentSubmissions;

Python

classroom/snippets/classroom_list_submissions.py
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def classroom_list_submissions(course_id, coursework_id):
  """
  Creates the courses the user has access to.
  Load pre-authorized user credentials from the environment.
  TODO(developer) - See https://developers.google.com/identity
  for guides on implementing OAuth2 for the application.
  """

  creds, _ = google.auth.default()
  # pylint: disable=maybe-no-member
  submissions = []
  page_token = None

  try:
    service = build("classroom", "v1", credentials=creds)
    while True:
      coursework = service.courses().courseWork()
      response = (
          coursework.studentSubmissions()
          .list(
              pageToken=page_token,
              courseId=course_id,
              courseWorkId=coursework_id,
              pageSize=10,
          )
          .execute()
      )
      submissions.extend(response.get("studentSubmissions", []))
      page_token = response.get("nextPageToken", None)
      if not page_token:
        break

    if not submissions:
      print("No student submissions found.")

    print("Student Submissions:")
    for submission in submissions:
      print(
          "Submitted at:"
          f"{(submission.get('id'), submission.get('creationTime'))}"
      )

  except HttpError as error:
    print(f"An error occurred: {error}")
    submissions = None
  return submissions


if __name__ == "__main__":
  # Put the course_id and coursework_id of course whose list needs to be
  # submitted.
  classroom_list_submissions(453686957652, 466086979658)

다음 샘플과 같이 userId 매개변수를 지정하여 특정 학생에게 속한 StudentSubmission 리소스를 가져옵니다.

Java

classroom/snippets/src/main/java/ListStudentSubmissions.java
List<StudentSubmission> studentSubmissions = new ArrayList<>();
String pageToken = null;

try {
  do {
    // Set the userId as a query parameter on the request.
    ListStudentSubmissionsResponse response =
        service
            .courses()
            .courseWork()
            .studentSubmissions()
            .list(courseId, courseWorkId)
            .setPageToken(pageToken)
            .set("userId", userId)
            .execute();

    /* Ensure that the response is not null before retrieving data from it to avoid errors. */
    if (response.getStudentSubmissions() != null) {
      studentSubmissions.addAll(response.getStudentSubmissions());
      pageToken = response.getNextPageToken();
    }
  } while (pageToken != null);

  if (studentSubmissions.isEmpty()) {
    System.out.println("No student submission found.");
  } else {
    for (StudentSubmission submission : studentSubmissions) {
      System.out.printf("Student submission: %s.\n", submission.getId());
    }
  }

Python

classroom/snippets/classroom_list_student_submissions.py
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def classroom_list_student_submissions(course_id, coursework_id, user_id):
  """
  Creates the courses the user has access to.
  Load pre-authorized user credentials from the environment.
  TODO(developer) - See https://developers.google.com/identity
  for guides on implementing OAuth2 for the application.
  """

  creds, _ = google.auth.default()
  # pylint: disable=maybe-no-member
  submissions = []
  page_token = None

  try:
    service = build("classroom", "v1", credentials=creds)
    while True:
      coursework = service.courses().courseWork()
      response = (
          coursework.studentSubmissions()
          .list(
              pageToken=page_token,
              courseId=course_id,
              courseWorkId=coursework_id,
              userId=user_id,
          )
          .execute()
      )
      submissions.extend(response.get("studentSubmissions", []))
      page_token = response.get("nextPageToken", None)
      if not page_token:
        break

    if not submissions:
      print("No student submissions found.")

    print("Student Submissions:")
    for submission in submissions:
      print(
          "Submitted at:"
          f"{(submission.get('id'), submission.get('creationTime'))}"
      )

  except HttpError as error:
    print(f"An error occurred: {error}")
  return submissions


if __name__ == "__main__":
  # Put the course_id, coursework_id and user_id of course whose list needs
  # to be submitted.
  classroom_list_student_submissions(453686957652, 466086979658, "me")

학생은 Google Admin SDK에서 반환하는 사용자의 고유 ID 또는 이메일 주소로 식별됩니다. 현재 사용자는 "me" 약식을 사용하여 자신의 ID를 참조할 수도 있습니다.

과정 내 모든 과제에 대한 학생 제출물을 받을 수도 있습니다. 이렇게 하려면 다음 샘플과 같이 리터럴 "-"courseWorkId로 사용합니다.

Java

service.courses().courseWork().studentSubmissions()
    .list(courseId, "-")
    .set("userId", userId)
    .execute();

Python

service.courses().courseWork().studentSubmissions().list(
    courseId=<course ID or alias>,
    courseWorkId='-',
    userId=<user ID>).execute()

필요한 범위는 과정에서 요청하는 사용자의 역할에 따라 다릅니다. 사용자가 교사 또는 도메인 관리자인 경우 다음 범위를 사용합니다.

  • https://www.googleapis.com/auth/classroom.coursework.students.readonly
  • https://www.googleapis.com/auth/classroom.coursework.students

사용자가 학생인 경우 다음 범위를 사용하세요.

  • https://www.googleapis.com/auth/classroom.coursework.me.readonly
  • https://www.googleapis.com/auth/classroom.coursework.me

학생 제출물을 검색할 수 있는 권한이 있다고 해서 첨부파일 또는 첨부파일 메타데이터에 액세스할 수 있는 권한이 있는 것은 아닙니다. 실제로 관리자가 이 과정의 멤버가 아닌 경우 첨부된 드라이브 파일의 제목이 표시되지 않을 수 있습니다. 관리자가 사용자 파일에 액세스하도록 허용하려면 도메인 전체 위임 가이드를 참조하세요.

학생 응답에 첨부파일 추가하기

Link, DriveFile 또는 YouTubeVideo 리소스를 첨부하여 학생 제출물에 링크를 첨부할 수 있습니다. 다음 샘플과 같이 courses.courseWork.studentSubmissions.modifyAttachments를 사용하면 됩니다.

Java

classroom/snippets/src/main/java/ModifyAttachmentsStudentSubmission.java
StudentSubmission studentSubmission = null;
try {
  // Create ModifyAttachmentRequest object that includes a new attachment with a link.
  Link link = new Link().setUrl("https://en.wikipedia.org/wiki/Irrational_number");
  Attachment attachment = new Attachment().setLink(link);
  ModifyAttachmentsRequest modifyAttachmentsRequest =
      new ModifyAttachmentsRequest().setAddAttachments(Arrays.asList(attachment));

  // The modified studentSubmission object is returned with the new attachment added to it.
  studentSubmission =
      service
          .courses()
          .courseWork()
          .studentSubmissions()
          .modifyAttachments(courseId, courseWorkId, id, modifyAttachmentsRequest)
          .execute();

  /* Prints the modified student submission. */
  System.out.printf(
      "Modified student submission attachments: '%s'.\n",
      studentSubmission.getAssignmentSubmission().getAttachments());
} catch (GoogleJsonResponseException e) {
  // TODO (developer) - handle error appropriately
  GoogleJsonError error = e.getDetails();
  if (error.getCode() == 404) {
    System.out.printf(
        "The courseId (%s), courseWorkId (%s), or studentSubmissionId (%s) does "
            + "not exist.\n",
        courseId, courseWorkId, id);
  } else {
    throw e;
  }
} catch (Exception e) {
  throw e;
}
return studentSubmission;

Python

classroom/snippets/classroom_add_attachment.py
def classroom_add_attachment(course_id, coursework_id, submission_id):
  """
  Adds attachment to existing course with specific course_id.
  Load pre-authorized user credentials from the environment.
  TODO(developer) - See https://developers.google.com/identity
  for guides on implementing OAuth2 for the application.
  """
  creds, _ = google.auth.default()
  # pylint: disable=maybe-no-member
  request = {
      "addAttachments": [
          {"link": {"url": "http://example.com/quiz-results"}},
          {"link": {"url": "http://example.com/quiz-reading"}},
      ]
  }

  try:
    service = build("classroom", "v1", credentials=creds)
    while True:
      coursework = service.courses().courseWork()
      coursework.studentSubmissions().modifyAttachments(
          courseId=course_id,
          courseWorkId=coursework_id,
          id=submission_id,
          body=request,
      ).execute()

  except HttpError as error:
    print(f"An error occurred: {error}")


if __name__ == "__main__":
  # Put the course_id, coursework_id and submission_id of course in which
  # attachment needs to be added.
  classroom_add_attachment("course_id", "coursework_id", "me")

링크 첨부파일은 대상 URL로 정의되며 클래스룸에서 제목과 썸네일 이미지를 자동으로 가져옵니다. 각 참조 페이지에서 다른 자료에 대해 알아볼 수 있습니다.

StudentSubmission는 강의 교사 또는 이를 소유한 학생만 수정할 수 있습니다. 학생 제출물의 CourseWorkTypeASSIGNMENT인 경우에만 Materials를 첨부할 수 있습니다.

필요한 범위는 과정에서 요청하는 사용자의 역할에 따라 다릅니다. 사용자가 교사인 경우 다음 범위를 사용하세요.

  • https://www.googleapis.com/auth/classroom.coursework.students

사용자가 학생인 경우 다음 범위를 사용하세요.

  • https://www.googleapis.com/auth/classroom.coursework.me

학생 응답 상태 관리

학생의 응답은 제출되지 않거나 제출되거나 반환될 수 있습니다. StudentSubmission의 상태 필드는 현재 상태를 나타냅니다. 상태를 변경하려면 다음 메서드 중 하나를 호출합니다.

이러한 메서드는 모두 빈 본문을 사용합니다. 예:

Java

classroom/snippets/src/main/java/ReturnStudentSubmission.java
try {
  service
      .courses()
      .courseWork()
      .studentSubmissions()
      .classroomReturn(courseId, courseWorkId, id, null)
      .execute();
} catch (GoogleJsonResponseException e) {
  // TODO (developer) - handle error appropriately
  GoogleJsonError error = e.getDetails();
  if (error.getCode() == 404) {
    System.out.printf(
        "The courseId (%s), courseWorkId (%s), or studentSubmissionId (%s) does "
            + "not exist.\n",
        courseId, courseWorkId, id);
  } else {
    throw e;
  }
} catch (Exception e) {
  throw e;
}

Python

service.courses().courseWork().studentSubmission().turnIn(
    courseId=<course ID or alias>,
    courseWorkId=<courseWork ID>,
    id=<studentSubmission ID>,
    body={}).execute()

StudentSubmission를 소유한 학생만 과제를 제출하거나 회수할 수 있습니다. 제출한 항목만 회수할 수 있습니다. 과정 교사는 제출됨 상태인 StudentSubmission만 반환할 수 있습니다.

학생 응답 채점하기

StudentSubmission 리소스에는 성적을 저장하는 두 개의 필드, 즉 학생에게 보고되는 성적인 assignedGrade와 교사에게만 표시되는 임시 성적인 draftGrade 필드가 있습니다. 이러한 필드는 다음 샘플과 같이 적절한 필드가 포함된 필드 마스크와 함께 courses.courseWork.studentSubmissions.patch를 사용하여 업데이트됩니다.

Java

classroom/snippets/src/main/java/PatchStudentSubmission.java
StudentSubmission studentSubmission = null;
try {
  // Updating the draftGrade and assignedGrade fields for the specific student submission.
  StudentSubmission content =
      service
          .courses()
          .courseWork()
          .studentSubmissions()
          .get(courseId, courseWorkId, id)
          .execute();
  content.setAssignedGrade(90.00);
  content.setDraftGrade(80.00);

  // The updated studentSubmission object is returned with the new draftGrade and assignedGrade.
  studentSubmission =
      service
          .courses()
          .courseWork()
          .studentSubmissions()
          .patch(courseId, courseWorkId, id, content)
          .set("updateMask", "draftGrade,assignedGrade")
          .execute();

  /* Prints the updated student submission. */
  System.out.printf(
      "Updated student submission draft grade (%s) and assigned grade (%s).\n",
      studentSubmission.getDraftGrade(), studentSubmission.getAssignedGrade());
} catch (GoogleJsonResponseException e) {
  // TODO (developer) - handle error appropriately
  GoogleJsonError error = e.getDetails();
  if (error.getCode() == 404) {
    System.out.printf(
        "The courseId (%s), courseWorkId (%s), or studentSubmissionId (%s) does "
            + "not exist.\n",
        courseId, courseWorkId, id);
  } else {
    throw e;
  }
} catch (Exception e) {
  throw e;
}
return studentSubmission;

Python

studentSubmission = {
  'assignedGrade': 99,
  'draftGrade': 80
}
service.courses().courseWork().studentSubmissions().patch(
    courseId=<course ID or alias>,
    courseWorkId=<courseWork ID>,
    id=<studentSubmission ID>,
    updateMask='assignedGrade,draftGrade',
    body=studentSubmission).execute()

클래스룸 UI를 사용하는 경우 교사는 먼저 임시 성적을 저장하기 전에는 성적을 할당할 수 없습니다. 그러면 할당된 성적을 학생에게 반환할 수 있습니다. 애플리케이션은 이 동작을 에뮬레이션해야 합니다. 애플리케이션은 다음 두 가지 방법 중 하나로 학생의 과제를 채점할 수 있습니다.

  • draftGrade만 할당합니다. 이 기능은 예를 들어 교사가 성적을 직접 검토하도록 하는 데 유용합니다. 학생은 임시 성적을 볼 수 없습니다.

  • 과제를 완전히 채점하려면 draftGradeassignedGrade를 모두 할당합니다.

할당된 성적 나열

courses.courseWork.studentSubmissions.list 메서드의 응답 객체를 탐색하여 특정 학습 과제 항목의 모든 성적을 나열할 수 있습니다.

Java

classroom/snippets/src/main/java/ListStudentSubmissions.java
  ListStudentSubmissionsResponse response =
      service
          .courses()
          .courseWork()
          .studentSubmissions()
          .list(courseId, courseWorkId)
          .setPageToken(pageToken)
          .execute();

  /* Ensure that the response is not null before retrieving data from it to avoid errors. */
  if (response.getStudentSubmissions() != null) {
    studentSubmissions.addAll(response.getStudentSubmissions());
    pageToken = response.getNextPageToken();
  }
} while (pageToken != null);

if (studentSubmissions.isEmpty()) {
  System.out.println("No student submissions found.");
} else {
  for (StudentSubmission submission : studentSubmissions) {
    System.out.printf(
        "User ID %s, Assigned grade: %s\n",
        submission.getUserId(), submission.getAssignedGrade());
  }
}

Python

response = coursework.studentSubmissions().list(
    courseId=course_id,
    courseWorkId=coursework_id,
    pageSize=10).execute()
submissions.extend(response.get('studentSubmissions', []))

if not submissions:
    print('No student submissions found.')

print('Student Submissions:')
for submission in submissions:
    print(f"Submitted at:"
          f"{(submission.get('userId'), submission.get('assignedGrade'))}")