Ek notları ve geri verilen notları geri verme

Bu, Classroom eklentileri açıklamalı kılavuz serisinin altıncı bölümüdür.

Bu adım adım açıklamalı kılavuzda, önceki adım adım açıklamalı kılavuzdaki örneği değiştirerek notlandırılmış etkinlik türündeki bir ek oluşturursunuz. Ayrıca, notu programlı olarak Google Classroom'a geri gönderirsiniz. Bu not, öğretmenin not defterinde taslak not olarak görünür.

Bu adım adım açıklamalı kılavuz, notları Classroom'a geri göndermeyle ilgili iki olası yaklaşım sunması açısından serideki diğerlerinden biraz farklıdır. Her ikisinin de geliştirici ve kullanıcı deneyimi üzerinde farklı etkileri vardır. Classroom eklentinizi tasarlarken her ikisini de göz önünde bulundurun. Uygulama seçenekleri hakkında daha fazla bilgi için Eklerle etkileşim kurma kılavuz sayfamızı okuyun.

API'deki notlandırma özelliklerinin isteğe bağlı olduğunu unutmayın. Herhangi bir etkinlik türündeki ek ile kullanılabilir.

Bu adım adım açıklamalı kılavuzda şunları tamamlarsınız:

  • Classroom API'ye gönderilen önceki ek oluşturma isteklerini değiştirerek ekin not paydasını da ayarlayın.
  • Öğrencinin gönderimini programatik olarak puanlayın ve ekteki notun payını ayarlayın.
  • Oturum açmış veya çevrimdışı durumdaki öğretmen kimlik bilgilerini kullanarak ödevin notunu Classroom'a iletmek için iki yaklaşım uygulayın.

Tamamlandığında, geri verilen gösterim davranışı tetiklendikten sonra notlar Classroom not defterinde görünür. Bu işlemin tam olarak ne zaman gerçekleşeceği, uygulama yaklaşımına bağlıdır.

Bu örnek için, önceki adım adım açıklamalı kılavuzda yer alan ve öğrenciye ünlü bir yer işaretinin resminin gösterildiği ve adını girmesinin istendiği etkinliği yeniden kullanın. Öğrenci doğru adı girerse ek için tam not atayın. Aksi durumda sıfır yazın.

Classroom eklentileri API notlandırma özelliğini anlama

Eklentiniz, bir ek için hem not payını hem de not paydasını ayarlayabilir. Bunlar, sırasıyla API'de pointsEarned ve maxPoints değerleri kullanılarak ayarlanır. Classroom kullanıcı arayüzündeki ek kartında, ayarlanmışsa maxPoints değeri gösterilir.

Bir ödevde maxPoints içeren birden fazla ek örneği

Şekil 1. maxPoints ayarının bulunduğu üç eklenti eki kartının yer aldığı ödev oluşturma kullanıcı arayüzü.

Classroom eklentileri API'si, ek notları için ayarları yapılandırmanıza ve kazanılan puanı belirlemenize olanak tanır. Bu notlar, ödevi notlarıyla aynı değildir. Ancak ödev notu ayarları, ek kartında Not senkronizasyonu etiketi bulunan ekin not ayarlarını takip eder. "Not senkronizasyonu" eki, bir öğrenci gönderimi için pointsEarned değerini ayarlarken öğrencinin ödev için taslak notunu da ayarlar.

Genellikle, ödeve eklenen ve maxPoints değerini ayarlayan ilk ek "Not senkronizasyonu" etiketini alır. "Not senkronizasyonu" etiketi örneği için Şekil 1'de gösterilen ödev oluşturma kullanıcı arayüzü örneğine bakın. "Ek 1" kartında "Not senkronizasyonu" etiketinin bulunduğunu ve kırmızı kutudaki ödev notunun 50 puana güncellendiğini unutmayın. Şekil 1'de üç ek kartı gösterilmesine rağmen yalnızca bir kartın "Not senkronizasyonu" etiketine sahip olduğunu da unutmayın. Mevcut uygulamanın önemli bir sınırlaması: "Not senkronizasyonu" etiketi yalnızca bir eke eklenebilir.

maxPoints ayarı yapılmış birden fazla ek varsa "Not senkronizasyonu" ayarını içeren ekin kaldırılması, kalan eklerde "Not senkronizasyonu" özelliğinin etkinleştirilmesini sağlamaz. maxPoints değerini ayarlayan başka bir ek eklemek, yeni ekte Not Senkronizasyonu'nu etkinleştirir ve maksimum ödev notu eşleşecek şekilde ayarlanır. Hangi eklerin "Not senkronizasyonu" etiketine sahip olduğunu veya belirli bir ödevin kaç eki olduğunu programatik olarak görmenizi sağlayan bir mekanizma yoktur.

Eklerin maksimum notunu ayarlama

Bu bölümde, ek notu için payda (yani tüm öğrencilerin gönderimleri için alabileceği maksimum puan) ayarlama işlemi açıklanmaktadır. Bunun için ekin maxPoints değerini ayarlayın.

Notlandırma özelliklerini etkinleştirmek için mevcut uygulamamızda yalnızca küçük bir değişiklik yapılması gerekiyor. Ekleme oluştururken maxPoints değerini studentWorkReviewUri, teacherViewUri ve diğer ek alanları içeren aynı AddOnAttachment nesnesine ekleyin.

Yeni bir ödevin varsayılan maksimum puanının 100 olduğunu unutmayın. Notların doğru şekilde ayarlandığını doğrulayabilmek için maxPoints değerini 100'den farklı bir değere ayarlamanızı öneririz. maxPoints değerini 50 olarak ayarlayarak gösterelim:

Python

maxPoints alanını, attachment nesnesini oluştururken courses.courseWork.addOnAttachments uç noktasına CREATE isteği göndermeden hemen önce ekleyin. Bu değeri, verdiğimiz örnekte olduğu gibi webapp/attachment_routes.py dosyasında bulabilirsiniz.

attachment = {
    # Specifies the route for a teacher user.
    "teacherViewUri": {
        "uri":
            flask.url_for(
                "load_activity_attachment",
                _scheme='https',
                _external=True),
    },
    # Specifies the route for a student user.
    "studentViewUri": {
        "uri":
            flask.url_for(
                "load_activity_attachment",
                _scheme='https',
                _external=True)
    },
    # Specifies the route for a teacher user when the attachment is
    # loaded in the Classroom grading view.
    "studentWorkReviewUri": {
        "uri":
            flask.url_for(
                "view_submission", _scheme='https', _external=True)
    },
    # Sets the maximum points that a student can earn for this activity.
    # This is the denominator in a fractional representation of a grade.
    "maxPoints": 50,
    # The title of the attachment.
    "title": f"Attachment {attachment_count}",
}

Bu gösterim için maxPoints değerini yerel Ek veritabanınızda da depolarsınız. Bu sayede, öğrenci gönderimlerini notlarken daha sonra ek bir API çağrısı yapmak zorunda kalmazsınız. Ancak, öğretmenlerin ödev not ayarlarını eklentinizden bağımsız olarak değiştirebileceğini de unutmayın. Atama düzeyindeki maxPoints değerini görmek için courses.courseWork uç noktasına bir GET isteği gönderin. Bunu yaparken CourseWork.id alanındaki itemId öğesini iletin.

Ardından, veritabanı modelinizi ekteki maxPoints değerini de içerecek şekilde güncelleyin. CREATE yanıtındaki maxPoints değerini kullanmanızı öneririz:

Python

İlk olarak Attachment tablosuna bir max_points alanı ekleyin. Bu değeri, verdiğimiz örnekte olduğu gibi webapp/models.py dosyasında bulabilirsiniz.

# Database model to represent an attachment.
class Attachment(db.Model):
    # The attachmentId is the unique identifier for the attachment.
    attachment_id = db.Column(db.String(120), primary_key=True)

    # The image filename to store.
    image_filename = db.Column(db.String(120))

    # The image caption to store.
    image_caption = db.Column(db.String(120))

    # The maximum number of points for this activity.
    max_points = db.Column(db.Integer)

courses.courseWork.addOnAttachments CREATE isteğine dönün. Yanıtta döndürülen maxPoints değerini saklayın.

new_attachment = Attachment(
    # The new attachment's unique ID, returned in the CREATE response.
    attachment_id=resp.get("id"),
    image_filename=key,
    image_caption=value,
    # Store the maxPoints value returned in the response.
    max_points=int(resp.get("maxPoints")))
db.session.add(new_attachment)
db.session.commit()

Ek artık maksimum notu aldı. Bu davranışı artık test edebilirsiniz. Yeni bir ödeve ek ekleyin ve ek kartında "Not senkronizasyonu" etiketinin gösterildiğini ve ödevin "Puan" değerinin değiştiğini gözlemleyin.

Classroom'da öğrenci gönderim notu ayarlama

Bu bölümde, ek notu için payda (öğrencinin ek için aldığı puan) ayarlama işlemi açıklanmaktadır. Bunun için öğrenci gönderiminin pointsEarned değerini belirleyin.

Şimdi vermeniz gereken önemli bir karar var: Eklentiniz pointsEarned ayarlama isteğini nasıl göndermelidir?

Sorun, pointsEarned ayarının teacher OAuth kapsamını gerektirmesidir. Öğrenci kullanıcılarına teacher kapsamı vermemeniz gerekir. Aksi takdirde, öğrenciler eklentinizle etkileşime geçtiğinde beklenmedik davranışlar (ör. Öğrenci Görünümü yerine Öğretmen Görünümü iFrame'inin yüklenmesi) görülebilir. Bu nedenle, pointsEarned değerini ayarlamak için iki seçeneğiniz vardır:

  • Giriş yapmış öğretmenin kimlik bilgilerini kullanarak.
  • Depolanmış (çevrimdışı) öğretmen kimlik bilgilerini kullanma.

Aşağıdaki bölümlerde, her bir uygulamayı göstermeden önce her yaklaşımın avantajları ve dezavantajları ele alınmaktadır. Verdiğimiz örneklerde, notları Classroom'a geçirmeye yönelik her iki yaklaşım da gösterilmektedir. Verilen örnekleri çalıştırırken nasıl bir yaklaşım seçeceğinizi öğrenmek için aşağıdaki dile özgü talimatlara bakın:

Python

webapp/attachment_routes.py dosyasının en üstündeki SET_GRADE_WITH_LOGGED_IN_USER_CREDENTIALS beyanını bulun. Oturum açmış öğretmenin kimlik bilgilerini kullanarak notları geri iletmek için bu değeri True olarak ayarlayın. Öğrenci etkinliği gönderdiğinde depolanan kimlik bilgilerini kullanarak notları geri göndermek için bu değeri False olarak ayarlayın.

Oturum açmış öğretmenin kimlik bilgilerini kullanarak notları ayarlama

pointsEarned ayarını yapma isteğinde bulunmak için oturum açmış kullanıcının kimlik bilgilerini kullanın. Bu, uygulamanın geri kalanını yansıttığı ve uygulanması için çok az çaba gerektirdiği için oldukça sezgisel görünecektir.

Ancak, öğretmenin yalnızca öğrencinin ödeviyle Öğrenci Çalışma İncelemesi iframe'inde etkileşimde bulunduğunu göz önünde bulundurun. Bunun bazı önemli etkileri vardır:

  • Öğretmen, Classroom kullanıcı arayüzünde bir işlem yapana kadar Classroom'da notlar doldurulmaz.
  • Öğretmenlerin, tüm öğrenci notlarını doldurmak için her öğrenci gönderimini açması gerekebilir.
  • Classroom'un notu alması ile Classroom kullanıcı arayüzünde notun görünmesi arasında kısa bir gecikme olur. Gecikme genellikle beş ila on saniyedir ancak 30 saniye kadar da sürebilir.

Bu faktörlerin bir araya gelmesi, öğretmenlerin bir sınıfın notlarını eksiksiz olarak doldurmak için önemli miktarda zaman alan manuel çalışma yapması gerektiği anlamına gelir.

Bu yaklaşımı uygulamak için mevcut Öğrenci Çalışması İncelemesi rotanıza bir ek API çağrısı ekleyin.

Öğrenci gönderimi ve ek kayıtlarını getirdikten sonra öğrencinin gönderimini değerlendirin ve elde edilen notu saklayın. AddOnAttachmentStudentSubmission nesnesinin pointsEarned alanında notu ayarlayın. Son olarak, istek metninde AddOnAttachmentStudentSubmission örneğini kullanarak courses.courseWork.addOnAttachments.studentSubmissions uç noktasına bir PATCH isteği gönderin. PATCH isteğimizdeki updateMask alanında pointsEarned değerini de belirtmemiz gerektiğini unutmayın:

Python

# Look up the student's submission in our database.
student_submission = Submission.query.get(flask.session["submissionId"])

# Look up the attachment in the database.
attachment = Attachment.query.get(student_submission.attachment_id)

grade = 0

# See if the student response matches the stored name.
if student_submission.student_response.lower(
) == attachment.image_caption.lower():
    grade = attachment.max_points

# Create an instance of the Classroom service.
classroom_service = ch._credential_handler.get_classroom_service()

# Build an AddOnAttachmentStudentSubmission instance.
add_on_attachment_student_submission = {
    # Specifies the student's score for this attachment.
    "pointsEarned": grade,
}

# Issue a PATCH request to set the grade numerator for this attachment.
patch_grade_response = classroom_service.courses().courseWork(
).addOnAttachments().studentSubmissions().patch(
    courseId=flask.session["courseId"],
    itemId=flask.session["itemId"],
    attachmentId=flask.session["attachmentId"],
    submissionId=flask.session["submissionId"],
    # updateMask is a list of fields being modified.
    updateMask="pointsEarned",
    body=add_on_attachment_student_submission).execute()

Çevrimdışı öğretmen kimlik bilgilerini kullanarak not verme

Not vermeyle ilgili ikinci yaklaşım, eki oluşturan öğretmen için saklanan kimlik bilgilerinin kullanılmasını gerektirir. Bu uygulama için, önceden yetkilendirilmiş bir öğretmenin yenileme ve erişim jetonlarını kullanarak kimlik bilgileri oluşturmanız ve ardından pointsEarned ayarlamak için bu kimlik bilgilerini kullanmanız gerekir.

Bu yaklaşımın önemli bir avantajı, Classroom kullanıcı arayüzünde notların öğretmenin işlem yapmasına gerek kalmadan doldurulmasıdır. Bu sayede yukarıda belirtilen sorunlar önlenir. Sonuç olarak son kullanıcılar, notlandırma deneyimini sorunsuz ve verimli olarak algılar. Ayrıca bu yaklaşım, notları hangi anı geri vereceğinizi (ör. öğrencilerin etkinliği veya eşzamansız olarak tamamlaması) seçmenize de olanak tanır.

Bu yaklaşımı uygulamak için aşağıdaki görevleri tamamlayın:

  1. Erişim jetonu depolamak için kullanıcı veritabanı kayıtlarını değiştirme
  2. Ek veritabanı kayıtlarını, öğretmen kimliğini depolayacak şekilde değiştirin.
  3. Öğretmenin kimlik bilgilerini alın ve (isteğe bağlı olarak) yeni bir Classroom hizmet örneği oluşturun.
  4. Gönderimin notunu belirleyebilirsiniz.

Bu gösterim için, öğrencinin etkinliği tamamladığında (öğrenci görünümü yolunda formu gönderdiğinde) verilecek notu ayarlayın.

Erişim jetonunu depolamak için kullanıcı veritabanı kayıtlarını değiştirme

API çağrıları yapmak için iki benzersiz jeton gerekir: yeniden yenileme jetonu ve erişim jetonu. Bu eğitim serisini şimdiye kadar takip ettiyseniz User tablo şemanızda zaten bir yenileme jetonu depolanmış olmalıdır. Kimlik doğrulama akışının bir parçası olarak erişim jetonu aldığınız için yalnızca oturum açmış kullanıcıyla API çağrıları yaptığınızda yenileme jetonunu depolamak yeterlidir.

Ancak, şimdi oturum açmış kullanıcı dışında biri olarak arama yapmanız gerekir. Bu, kimlik doğrulama akışının kullanılamayacağı anlamına gelir. Bu nedenle, erişim jetonunu yenileme jetonuyla birlikte saklamanız gerekir. User tablo şemanızı, erişim jetonu içerecek şekilde güncelleyin:

Python

Sağlanan örneğimizde bu, webapp/models.py dosyasındadır.

# Database model to represent a user.
class User(db.Model):
    # The user's identifying information:
    id = db.Column(db.String(120), primary_key=True)
    display_name = db.Column(db.String(80))
    email = db.Column(db.String(120), unique=True)
    portrait_url = db.Column(db.Text())

    # The user's refresh token, which will be used to obtain an access token.
    # Note that refresh tokens will become invalid if:
    # - The refresh token has not been used for six months.
    # - The user revokes your app's access permissions.
    # - The user changes passwords.
    # - The user belongs to a Google Cloud organization
    #   that has session control policies in effect.
    refresh_token = db.Column(db.Text())

    # An access token for this user.
    access_token = db.Column(db.Text())

Ardından, User kaydı oluşturan veya güncelleyen kodları, erişim jetonunu da depolayacak şekilde güncelleyin:

Python

Bu, verdiğimiz örnekte webapp/credential_handler.py dosyasındadır.

def save_credentials_to_storage(self, credentials):
    # Issue a request for the user's profile details.
    user_info_service = googleapiclient.discovery.build(
        serviceName="oauth2", version="v2", credentials=credentials)
    user_info = user_info_service.userinfo().get().execute()
    flask.session["username"] = user_info.get("name")
    flask.session["login_hint"] = user_info.get("id")

    # See if we have any stored credentials for this user. If they have used
    # the add-on before, we should have received login_hint in the query
    # parameters.
    existing_user = self.get_credentials_from_storage(user_info.get("id"))

    # If we do have stored credentials, update the database.
    if existing_user:
        if user_info:
            existing_user.id = user_info.get("id")
            existing_user.display_name = user_info.get("name")
            existing_user.email = user_info.get("email")
            existing_user.portrait_url = user_info.get("picture")

        if credentials and credentials.refresh_token is not None:
            existing_user.refresh_token = credentials.refresh_token
            # Update the access token.
            existing_user.access_token = credentials.token

    # If not, this must be a new user, so add a new entry to the database.
    else:
        new_user = User(
            id=user_info.get("id"),
            display_name=user_info.get("name"),
            email=user_info.get("email"),
            portrait_url=user_info.get("picture"),
            refresh_token=credentials.refresh_token,
            # Store the access token as well.
            access_token=credentials.token)

        db.session.add(new_user)

    db.session.commit()

Eklemeler veritabanı kayıtlarını öğretmen kimliği depolayacak şekilde değiştirme

Bir etkinliğe not vermek için pointsEarned adlı kullanıcıyı derste öğretmen olarak ayarlamak üzere çağrı yapın. Bunu yapmanın birkaç yolu vardır:

  • Öğretmen kimlik bilgilerinin ders kimliklerine yerel olarak eşlenmesini sağlayın. Ancak aynı öğretmenin her zaman belirli bir dersle ilişkili olmayabileceğini unutmayın.
  • Mevcut öğretmenleri almak için Classroom API courses uç noktasına GET istekleri gönderin. Ardından, eşleşen öğretmen kimlik bilgilerini bulmak için yerel kullanıcı kayıtlarını sorgulayın.
  • Eklenti eki oluştururken yerel ek veritabanında bir öğretmen kimliği depolayın. Ardından, Öğrenci Görünümü iframe'ine iletilen attachmentId'den öğretmen kimlik bilgilerini alın.

Bu örnekte, öğrenci bir etkinlik ekini tamamladığında not vereceğiniz için son seçenek gösterilmektedir.

Veritabanının Attachment tablosuna öğretmen kimliği alanı ekleyin:

Python

Bu, verdiğimiz örnekte webapp/models.py dosyasındadır.

# Database model to represent an attachment.
class Attachment(db.Model):
    # The attachmentId is the unique identifier for the attachment.
    attachment_id = db.Column(db.String(120), primary_key=True)

    # The image filename to store.
    image_filename = db.Column(db.String(120))

    # The image caption to store.
    image_caption = db.Column(db.String(120))

    # The maximum number of points for this activity.
    max_points = db.Column(db.Integer)

    # The ID of the teacher that created the attachment.
    teacher_id = db.Column(db.String(120))

Ardından, Attachment kaydı oluşturan veya güncelleyen tüm kodları, içerik üreticinin kimliğini de depolayacak şekilde güncelleyin:

Python

Bu, verdiğimiz örnekte webapp/attachment_routes.py dosyasındaki create_attachments yöntemindedir.

# Store the attachment by id.
new_attachment = Attachment(
    # The new attachment's unique ID, returned in the CREATE response.
    attachment_id=resp.get("id"),
    image_filename=key,
    image_caption=value,
    max_points=int(resp.get("maxPoints")),
    teacher_id=flask.session["login_hint"])
db.session.add(new_attachment)
db.session.commit()

Öğretmenin kimlik bilgilerini alma

Öğrenci Görünümü iframe'ini yayınlayan rotayı bulun. Öğrencinin yanıtını yerel veritabanınızda depoladıktan hemen sonra, yerel depolama alanınızdan öğretmenin kimlik bilgilerini alın. Önceki iki adımda yapılan hazırlık sayesinde bu işlem kolay olacaktır. Bunları, öğretmen kullanıcı için Classroom hizmetinin yeni bir örneğini oluşturmak amacıyla da kullanabilirsiniz:

Python

Bu, verdiğimiz örnekte webapp/attachment_routes.py dosyasındaki load_activity_attachment yöntemindedir.

# Create an instance of the Classroom service using the tokens for the
# teacher that created the attachment.

# We're assuming that there are already credentials in the session, which
# should be true given that we are adding this within the Student View
# route; we must have had valid credentials for the student to reach this
# point. The student credentials will be valid to construct a Classroom
# service for another user except for the tokens.
if not flask.session.get("credentials"):
    raise ValueError(
        "No credentials found in session for the requested user.")

# Make a copy of the student credentials so we don't modify the original.
teacher_credentials_dict = deepcopy(flask.session.get("credentials"))

# Retrieve the requested user's stored record.
teacher_record = User.query.get(attachment.teacher_id)

# Apply the user's tokens to the copied credentials.
teacher_credentials_dict["refresh_token"] = teacher_record.refresh_token
teacher_credentials_dict["token"] = teacher_record.access_token

# Construct a temporary credentials object.
teacher_credentials = google.oauth2.credentials.Credentials(
    **teacher_credentials_dict)

# Refresh the credentials if necessary; we don't know when this teacher last
# made a call.
if teacher_credentials.expired:
    teacher_credentials.refresh(Request())

# Request the Classroom service for the specified user.
teacher_classroom_service = googleapiclient.discovery.build(
    serviceName=CLASSROOM_API_SERVICE_NAME,
    version=CLASSROOM_API_VERSION,
    credentials=teacher_credentials)

Gönderimin notunu belirleme

Bu noktadan sonraki prosedür, oturum açmış öğretmenin kimlik bilgilerini kullanma ile aynıdır. Ancak aramayı, önceki adımda alınan öğretmen kimlik bilgileriyle yapmanız gerektiğini unutmayın:

Python

# Issue a PATCH request as the teacher to set the grade numerator for this
# attachment.
patch_grade_response = teacher_classroom_service.courses().courseWork(
).addOnAttachments().studentSubmissions().patch(
    courseId=flask.session["courseId"],
    itemId=flask.session["itemId"],
    attachmentId=flask.session["attachmentId"],
    submissionId=flask.session["submissionId"],
    # updateMask is a list of fields being modified.
    updateMask="pointsEarned",
    body=add_on_attachment_student_submission).execute()

Eklentiyi test etme

Önceki adım adım açıklamalı kılavuza benzer şekilde, öğretmen olarak etkinlik türündeki bir ek içeren bir ödev oluşturun, öğrenci olarak bir yanıt gönderin ve ardından gönderimini Öğrenci Çalışması İncelemesi iFrame'inde açın. Notu, uygulama yaklaşımınıza bağlı olarak farklı zamanlarda görebilirsiniz:

  • Öğrenci etkinliği tamamladığında bir notu geri vermeyi seçtiyseniz Öğrenci Çalışma İncelemesi iframe'ini açmadan önce geçici notunu kullanıcı arayüzünde görürsünüz. Bunu ödevi açarken öğrenci listesinde ve Öğrenci Çalışması İnceleme iframe'inin yanındaki "Not" kutusunda da görebilirsiniz.
  • Öğretmen Öğrenci Çalışması İncelemesi iframe'ini açtığında bir notu geri vermeyi seçtiyseniz not, iframe yüklendikten kısa bir süre sonra "Not" kutusunda görünür. Yukarıdaki gibi bu işlem 30 saniye sürebilir. Ardından, belirli bir öğrencinin notu, diğer Classroom not defteri görünümlerinde de görünecektir.

Öğrenci için doğru puanın gösterildiğini onaylayın.

Tebrikler! Bir sonraki adıma geçmeye hazırsınız: Google Classroom dışında ek oluşturma.