Gerenciar atividades e notas

A IU do Google Sala de Aula é compatível com cinco tipos de atividades: "Atividades", "Atividades com teste", "Perguntas para resposta curta", "Perguntas de múltipla escolha" e Materiais. Atualmente, a API Classroom oferece suporte a três desses tipos, que são conhecidos como CourseWorkType na API: Atividades, Perguntas de resposta curta e Perguntas de múltipla escolha.

Para acessar essa funcionalidade, use o recurso CourseWork, que representa uma tarefa ou pergunta atribuída aos alunos de um curso específico, incluindo todos os materiais e detalhes adicionais, como data de entrega ou pontuação máxima.

Além do recurso CourseWork, você pode gerenciar atividades concluídas com o recurso StudentSubmission. As seções abaixo descrevem isso em mais detalhes.

Criar atividades

As atividades podem ser criadas em nome dos professores do curso, e tentar criar atividades em um curso em nome de um estudante resulta em um erro 403 PERMISSION_DENIED. Da mesma forma, os administradores de domínio também não podem criar atribuições para cursos que não ensinam e tentar fazer isso pela API também resultará em um erro 403 PERMISSION_DENIED.

Ao criar atividades usando o método courses.courseWork.create, você pode anexar links como materials, mostrado no exemplo de código abaixo:

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

sala de aula/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)

O resultado inclui um identificador atribuído pelo servidor que pode ser usado para fazer referência à atribuição em outras solicitações da API.

Para incluir materiais vinculados em uma atividade criada na API Classroom, use o recurso Link, especificando o URL de destino. O Google Sala de Aula busca automaticamente o título e a imagem em miniatura. A API Classroom também oferece suporte nativo aos materiais do Google Drive e do YouTube, que podem ser incluídos com um recurso DriveFile ou YouTubeVideo de maneira semelhante.

Para especificar uma data de conclusão, defina os campos dueDate e dueTime como o horário UTC correspondente. A data de conclusão precisa estar no futuro.

Recuperar atividades e perguntas

É possível recuperar atividades e perguntas para alunos e professores do curso correspondente ou por um administrador de domínio. Para recuperar uma atividade ou pergunta específica, use cursos.courseWork.get. Para recuperar todas as atividades ou perguntas (opcionalmente corresponder a alguns critérios), use courses.courseWork.list.

O escopo necessário depende da função que o usuário solicitante tem no curso. Se o usuário for um estudante, use um dos seguintes escopos:

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

Se o usuário for um professor ou um administrador de domínio, use um dos seguintes escopos:

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

Ter permissão para recuperar uma atividade ou pergunta não implica permissão para acessar materiais ou metadados do material. Na prática, isso significa que um administrador talvez não veja o título de um arquivo do Drive anexado se não for membro do curso. Se você quer permitir que os administradores acessem arquivos de usuários, consulte o guia delegação em todo o domínio.

Gerenciar as respostas dos estudantes

Um recurso StudentSubmission representa o trabalho realizado e a nota de um estudante em uma atividade ou pergunta. Um recurso StudentSubmission é criado implicitamente para cada estudante quando uma nova pergunta ou atividade é criada.

As seções a seguir explicam as ações comuns que gerenciam as respostas dos estudantes.

Recuperar as respostas dos estudantes

Os estudantes podem recuperar os próprios envios, os professores podem recuperar os envios de todos os estudantes dos cursos e os administradores do domínio podem recuperar todos os envios de todos os estudantes no domínio. Cada envio de estudante recebe um identificador. Se você souber o identificador, use courses.courseWork.studentSubmissions.get para recuperá-lo.

Use o método courses.courseWork.studentSubmissions.list para receber recursos StudentSubmission que correspondam a alguns critérios, conforme mostrado no exemplo a seguir:

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

sala de aula/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)

Recupere recursos StudentSubmission que pertencem a um aluno específico especificando o parâmetro userId, conforme mostrado no exemplo a seguir:

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")

Os estudantes são identificados pelo ID ou endereço de e-mail exclusivo do usuário, conforme retornado pelo SDK Admin do Google. O usuário atual também pode se referir ao próprio ID usando a abreviação "me".

Também é possível receber os envios dos alunos para todas as tarefas de um curso. Para fazer isso, use o "-" literal como courseWorkId, conforme mostrado no exemplo a seguir:

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()

O escopo necessário depende da função que o usuário solicitante tem no curso. Use o escopo a seguir se o usuário for um professor ou um administrador do domínio:

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

Use o escopo a seguir se o usuário for um estudante:

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

Ter permissão para recuperar os envios de um estudante não implica permissão para acessar anexos ou metadados de anexos. Na prática, isso significa que um administrador talvez não veja o título de um arquivo do Drive anexado se não for membro do curso. Se você quiser permitir que os administradores acessem os arquivos do usuário, consulte o guia delegação em todo o domínio.

Adicionar anexos a uma resposta de estudante

Para anexar links ao envio de um estudante, anexe um recurso Link, DriveFile ou YouTubeVideo. Isso é feito com courses.courseWork.studentSubmissions.modifyAttachments, conforme mostrado no exemplo a seguir:

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

sala de aula/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")

Um anexo de link é definido pelo URL de destino. O Google Sala de Aula vai buscar automaticamente o título e a imagem em miniatura. Você pode aprender sobre os outros materiais nas respectivas páginas de referência.

O StudentSubmission só pode ser modificado pelo professor do curso ou pelo aluno proprietário dele. Você só pode anexar Materials se CourseWorkType do envio do estudante for ASSIGNMENT.

O escopo necessário depende da função que o usuário solicitante tem no curso. Use o escopo a seguir se o usuário for um professor:

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

Use o escopo a seguir se o usuário for um estudante:

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

Gerenciar o estado de resposta do estudante

A resposta de um estudante pode ser cancelada, entregue ou devolvida. O campo do estado em StudentSubmission indica o estado atual. Para mudar o estado, chame um dos seguintes métodos:

Todos esses métodos têm um corpo vazio. Exemplos

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()

Só o estudante que é o proprietário de uma StudentSubmission pode entregá-la ou recuperá-la. Apenas envios entregues podem ser reivindicados. Os professores do curso só podem retornar uma StudentSubmission que esteja no estado "entregue".

Avaliar as respostas dos estudantes

O recurso StudentSubmission tem dois campos para armazenar notas: assignedGrade, que é a nota informada aos estudantes, e draftGrade, que é uma nota provisória visível apenas para os professores. Esses campos são atualizados usando courses.courseWork.studentSubmissions.patch com uma máscara de campo contendo os campos apropriados, conforme mostrado no exemplo abaixo.

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()

Ao trabalhar com a IU do Google Sala de Aula, os professores só podem atribuir notas depois de salvarem uma nota temporária. A nota atribuída pode ser devolvida para o estudante. Os aplicativos precisam emular esse comportamento. Seu aplicativo pode avaliar a atividade de um estudante de duas maneiras:

  • Atribua apenas a draftGrade. Isso é útil, por exemplo, para permitir que o professor revise as notas manualmente antes de finalizá-las. Os alunos não podem ver as notas temporárias.

  • Atribua a draftGrade e a assignedGrade para atribuir nota total a uma atividade.

Listar notas atribuídas

É possível listar todas as notas de um determinado item do curso explorando o objeto de resposta do método 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'))}")