ניהול העבודות והציונים

בממשק המשתמש של Classroom יש תמיכה בחמישה סוגים של עבודות: מטלות, מטלות בוחן, שאלות עם תשובות קצרות, שאלות אמריקאיות וחומרים. נכון לעכשיו, ב-Classroom API יש תמיכה בשלושה מהסוגים האלה (CourseWorkType): מטלות, שאלות עם תשובות קצרות ושאלות אמריקאיות.

Materials לCourseWorkType משאבים: מטלות, שאלות עם תשובות קצרות ושאלות אמריקאיות.

כדי לקבל גישה לפונקציונליות הזו, תוכלו להשתמש במשאב 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, צריך להשתמש במשאב קישור ולציין את כתובת היעד. המערכת של Classroom מאחזרת באופן אוטומטי את השם ואת התמונה הממוזערת של הסרטון. ממשק ה-API של Classroom תומך גם הוא במקור ב-Google Drive ובחומרים של YouTube, שניתן לכלול במשאב DriveFile או במשאב YouTubeVideo באופן דומה.

כדי לציין תאריך יעד, צריך להגדיר בשדות dueDate ו-dueTime את השעה המקבילה לפי שעון UTC. תאריך היעד חייב להיות בעתיד.

אחזור מטלות ושאלות

תוכלו לאחזר את המטלות והשאלות של תלמידים ומורים בקורס הרלוונטי או על ידי אדמין דומיין. כדי לאחזר מטלות או שאלות ספציפיות, תוכלו להיעזר ב-courses.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

אחזור של מטלה או שאלה לא מעיד על הרשאות גישה לחומרים או למטא-נתונים של חומרים. בפועל, המשמעות היא שאדמין לא יכול לראות את השם של קובץ Drive מצורף אם הוא לא חבר בקורס. תוכלו להיעזר במדריך הענקת גישה ברמת הדומיין כדי לתת לאדמינים גישה לקובצי משתמשים.

ניהול תשובות התלמידים

במשאב 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)

מאחזרים StudentSubmission משאבים ששייכים לתלמיד מסוים על ידי ציון הפרמטר userId, כמו בדוגמה הבאה:

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. המשתמש הנוכחי יכול גם להפנות למזהה שלו באמצעות הקיצור "me".

אפשר גם לקבל הגשות של תלמידים עבור כל המטלות בקורס. לשם כך, עליכם להשתמש במשתנה "-" בתור 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

גם אם אתם מקבלים גישה לחומרים של התלמידים והתלמידות, זה לא אומר שיש להם הרשאות לגשת לקבצים מצורפים או למטא-נתונים של קבצים מצורפים. בפועל, המשמעות היא שאדמין לא יכול לראות את השם של קובץ Drive מצורף אם הוא לא חבר בקורס. תוכלו להיעזר במדריך הענקת גישה ברמת הדומיין כדי להעניק לאדמינים גישה לקובצי משתמשים.

הוספת קבצים מצורפים לתשובה של התלמיד/ה

כדי לצרף קישורים לטופס שתלמידים הגישו, אפשר לצרף אליו משאב 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")

הגדרת קובץ של קישור מתבצעת לפי כתובת אתר היעד. ב-Classroom, המערכת תאחזר באופן אוטומטי את השם ואת התמונה הממוזערת של הסרטון. אפשר ללמוד על החומרים האחרים בדפי העזר הרלוונטיים.

רק מורה בקורס או תלמיד שבבעלותו יכולים לשנות את StudentSubmission. אפשר לצרף את הפרמטר Materials רק אם הערך בשדה CourseWorkType של הקובץ הסטודנט הוא ASSIGNMENT.

ההיקף הנדרש תלוי בתפקיד של המשתמש המבקש בקורס. אם המשתמש הוא מורה, צריך להשתמש בהיקף הבא:

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

כשמשתמשים בממשק המשתמש של Classroom, מורים לא יכולים להקצות ציון לפני שהם שומרים טיוטת ציון. אחר כך תוכלו להחזיר לתלמיד את הציון שהוקצה לו. אפליקציות צריכות לבצע אמולציה של ההתנהגות הזו. הבקשה יכולה לתת ציון למטלה של תלמיד באחת משתי דרכים:

  • יש להקצות רק את draftGrade. כך תוכלו, למשל, לאפשר למורה לעיין ידנית בציונים לפני שהם מסיימים אותם. תלמידים לא יכולים לראות טיוטות ציונים.

  • כדי לתת ציון מלא למטלה, צריך להקצות גם את draftGrade וגם את assignedGrade.

הצגת רשימה של הציונים שהוקצו

כדי להציג רשימה של כל הציונים לעבודת כיתה מסוימת, אפשר לבדוק את אובייקט התגובה של השיטה 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'))}")