Gerenciar atividades e notas

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

Para acessar essa funcionalidade, você pode usar o recurso CourseWork, que representa uma tarefa ou pergunta que foi atribuída a alunos em um curso específico, incluindo materiais e detalhes adicionais, como data de entrega ou pontuação máxima.

Além do recurso CourseWork, é possível gerenciar as tarefas concluídas com o recurso StudentSubmission. As seções a seguir descrevem isso em mais detalhes.

Criar atribuições

As tarefas podem ser criadas apenas em nome dos professores do curso, e tentar criar tarefas em um curso em nome de um aluno resultará 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 por meio da API também resultará em um erro PERMISSION_DENIED 403.

Ao criar atribuições usando o método courses.courseWork.create, você pode anexar links como materials, mostrado na amostra de código abaixo:

Java

sala de aula/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
from __future__ import print_function

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 referenciar a atribuição em outras solicitações da API.

Para incluir materiais vinculados em uma tarefa criada por meio da Classroom API, use um 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 tem compatibilidade nativa com materiais do Google Drive e do YouTube, que podem ser incluídos com um recurso DriveFile ou recurso 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 entrega precisa estar no futuro.

Recuperar tarefas e perguntas

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

O escopo necessário depende do papel que o usuário solicitante tem no curso. Se o usuário for aluno, 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 atribuição ou pergunta não implica permissões para acessar materiais ou metadados do material. Na prática, isso significa que um administrador pode não ver 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 sobre delegação em todo o domínio.

Gerenciar as respostas dos alunos

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

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

Recuperar respostas dos alunos

Os alunos podem recuperar os próprios envios, os professores podem recuperar envios de todos os alunos nos cursos, e os administradores do domínio podem recuperar todos os envios de todos os alunos no domínio. Cada envio de aluno 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 correspondem a alguns critérios, conforme mostrado no exemplo a seguir:

Java

sala de aula/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
from __future__ import print_function

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(f"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

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

sala de aula/snippets/classroom_list_student_submissions.py
from __future__ import print_function

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(f"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 alunos são identificados pelo código exclusivo ou pelo endereço de e-mail 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 de todas as tarefas em 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 do papel que o usuário solicitante tem no curso. Use o seguinte escopo se o usuário for um professor ou um administrador de domínio:

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

Use o seguinte escopo se o usuário for aluno:

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

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

Adicionar anexos à resposta de um aluno

É possível anexar links a um envio de aluno anexando um recurso Link, DriveFile ou YouTubeVideo. Isso é feito com courses.courseWork.studentSubmissions.modifyAttachments, conforme mostrado no exemplo a seguir:

Java

sala de aula/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 busca automaticamente o título e a imagem em miniatura. Saiba mais sobre os outros materiais nas respectivas páginas de referência.

O StudentSubmission só pode ser modificado por um professor do curso ou pelo aluno que é o proprietário. Você só poderá anexar Materials se o CourseWorkType do envio do aluno for ASSIGNMENT.

O escopo necessário depende do papel que o usuário solicitante tem no curso. Use o seguinte escopo se o usuário for um professor:

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

Use o seguinte escopo se o usuário for aluno:

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

Gerenciar o estado das respostas dos alunos

É possível cancelar o envio, entregar ou devolver a resposta de um aluno. O campo de estado em StudentSubmission indica o estado atual. Para alterar o estado, chame um dos seguintes métodos:

Todos esses métodos têm um corpo vazio. Exemplo:

Java

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

Somente o aluno que possui o StudentSubmission pode entregá-lo ou recuperá-lo. Somente um envio pode ser reivindicado. Os professores do curso só podem retornar uma StudentSubmission que esteja no estado entregue.

Atribuir nota às respostas dos alunos

O recurso StudentSubmission tem dois campos para armazenar notas: assignedGrade, que é a nota informada para os alunos, e draftGrade, que é uma nota temporária visível apenas para professores. Esses campos são atualizados usando courses.courseWork.studentSubmissions.patch com uma máscara que contém os campos apropriados, conforme mostrado no exemplo a seguir.

Java

sala de aula/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 não podem atribuir uma nota antes de salvar a nota pela primeira vez. A nota atribuída pode ser devolvida a um aluno. Os aplicativos precisam emular esse comportamento. Seu aplicativo pode avaliar a tarefa de um aluno 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 dar nota total a uma atividade.

Listar notas atribuídas

É possível listar todas as notas de um item específico do curso explorando o objeto de resposta do método courses.courseWork.studentSubmissions.list:

Java

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