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 API Classroom oferece suporte a três desses tipos, que são conhecidos como CourseWorkType: Atividades, perguntas de resposta curta e perguntas de múltipla escolha.

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

Além do recurso WorkWork, é possível gerenciar as tarefas concluídas com o recurso StudentSubmission. As seções a seguir os descrevem mais detalhadamente.

Criar atividades

As atividades podem ser criadas apenas em nome dos professores do curso. Se você tentar criar tarefas em um curso em nome de um aluno, o resultado será um erro PERMISSION_DENIED 403. Da mesma forma, os administradores de domínios também não podem criar atribuições para cursos que não ensinam, e a tentativa de 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, mostrados no exemplo 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 fazer referência à atribuição em outras solicitações de API.

Para incluir materiais vinculados em uma atividade criada pela API Classroom, use um recurso de 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 é compatível nativamente com materiais do Google Drive e do YouTube, que podem ser incluídos em um recurso DriveFile ou recurso de vídeo do YouTube 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 atividades e perguntas

Você pode recuperar tarefas e perguntas para alunos e professores do curso correspondente ou por um administrador do domínio. Para recuperar uma atividade ou pergunta específica, use courses.courseWork.get. Para recuperar todas as atividades ou perguntas (opcionalmente correspondente 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 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 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õ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. Para permitir que os administradores acessem os arquivos de usuário, consulte o guia sobre delegação em todo o domínio.

Gerenciar as respostas dos estudantes

Um recurso StudentSubmission representa o trabalho feito 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 envios, os professores podem recuperar os envios de todos os estudantes nos cursos, e os administradores do domínio podem recuperar todos os envios de todos os alunos no domínio. Cada identificador 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 ver os recursos do 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 estudantes são identificados pelo ID exclusivo ou 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 estudantes para todas as atividades 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 do papel que o usuário solicitante tem no curso. Use o escopo a seguir se o usuário for um professor ou administrador de domínio:

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

Use este escopo se o usuário for estudante:

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

A permissão para recuperar o envio de um estudante não implica permissões para acessar anexos ou metadados de anexo. 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 de delegação em todo o domínio.

Adicionar anexos à resposta de um estudante

É possível anexar links a um envio de um estudante 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/ModifiedAttachmentsStudentSubmission.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. Saiba mais sobre os outros materiais nas respectivas páginas de referência.

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

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

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

Use este escopo se o usuário for estudante:

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

Gerenciar o estado da resposta do estudante

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

Todos esses métodos usam 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 estudante que é proprietário do StudentSubmission poderá entregá-lo ou recuperá-lo. Somente um envio pode ser reivindicado. Os professores do curso só podem retornar um StudentSubmission que esteja no estado entregue.

Dar nota às respostas dos estudantes

O recurso StudentSubmission tem dois campos para armazenar notas: assignedGrade, que é a nota informada aos 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 de campo contendo 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 só podem atribuir uma nota depois de salvar uma nota temporária. A nota atribuída pode ser devolvida a um 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 manualmente as notas antes de finalizá-las. Os estudantes não podem ver as notas temporárias.

  • Atribua draftGrade e assignedGrade para avaliar totalmente uma atividade.

Listar as 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

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