Bu, Classroom eklentileriyle ilgili adım adım açıklamalı serinin altıncı kılavuzudur.
Bu adım adım açıklamalı kılavuzda, önceki adım adım açıklamalı kılavuzda verilen örneği değiştirerek notlu etkinlik türü eki oluşturacaksınız. Ayrıca, programatik olarak bir notu Google Classroom'a geri gönderirsiniz. Bu not, öğretmenin not defterinde geçici not olarak görünür.
Bu adım adım açıklamalı kılavuz, notları Classroom'a geri aktarmayla ilgili iki olası yaklaşım olması açısından serinin diğer bölümlerinden 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 edinmek için Eklerle etkileşim kurma kılavuzu sayfamızı okuyun.
API'deki notlandırma özelliklerinin isteğe bağlı olduğunu unutmayın. Bunlar herhangi bir etkinlik türündeki ekle kullanılabilir.
Bu adım adım açıklamalı kılavuz kapsamında aşağıdaki işlemleri tamamlayacaksınız:
- Ekin paydasını da ayarlamak için önceki ek oluşturma isteklerini Classroom API'de değiştirin.
- Öğrencinin gönderimini programatik olarak puanlayın ve ekin not 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 durumun tam olarak ne zaman yaşanacağı uygulama yaklaşımına bağlıdır.
Bu örneğin amacı doğrultusunda, önceki adım adım açıklamalı kılavuzda yer alan etkinliği yeniden kullanın. Burada öğrenciye ünlü bir önemli noktanın resmini gösterip adını girmesi istenir. Öğrenci doğru adı girerse ek için tam not atayın. Aksi durumda sıfır yazın.
Classroom eklentileri API'sinin not verme ö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 bir ek kartında, ayarlandığında maxPoints
değeri gösterilir.
Şekil 1. maxPoints
ayarının ayarlandığı üç eklenti ek kartının bulunduğu ö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. Bunlar, ödev notlarıyla aynı değildir. Ancak ödev not ayarları, ek kartında Not senkronizasyonu etiketi bulunan ekin ek notu ayarlarına göre uygulanır. "Not senkronizasyonu" eki bir öğrenci gönderimi için pointsEarned
değerini ayarladığında, öğrencinin ödevle ilgili geçici notu da ayarlanır.
Genellikle, maxPoints
öğesini ayarlayan ödeve eklenen ilk ek, "Not senkronizasyonu" etiketini alır. "Not senkronizasyonu" etiketinin bir ö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 puan olarak güncellendiğini unutmayın. Ayrıca Şekil 1'de üç ek kartı gösterilse de yalnızca bir kartın "Not senkronizasyonu" etiketine sahip olduğunu unutmayın. Bu, mevcut uygulamanın önemli bir kısıtlamasıdır: Yalnızca bir ekte "Not senkronizasyonu" etiketi olabilir.
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 eklendiğinde, yeni ekte Not Senkronizasyonu etkinleştirilir ve maksimum ödev notu buna göre ayarlanır. Hangi ekin "Not senkronizasyonu" etiketine sahip olduğunu veya belirli bir ödevin kaç ekin olduğunu programatik olarak görmenin bir mekanizması yoktur.
Bir ekin maksimum notunu belirleme
Bu bölümde, ek notu için paydaşın ayarlanması açıklanmaktadır. Bu, tüm öğrencilerin gönderimleri için alabileceği maksimum puandır. Bunun için ekin maxPoints
değerini ayarlayın.
Not verme özelliklerinin etkinleştirilmesi için mevcut uygulamamızda yalnızca küçük bir değişiklik yapılması yeterlidir. Ek oluştururken maxPoints
değerini, studentWorkReviewUri
, teacherViewUri
ve diğer ek alanlarını içeren AddOnAttachment
nesnesine ekleyin.
Yeni bir ödev için varsayılan maksimum puanın 100 olduğunu unutmayın. Notların doğru şekilde ayarlandığını doğrulayabilmeniz için maxPoints
öğesini 100 dışında bir değere ayarlamanızı öneririz. Örnek olarak maxPoints
değerini 50 olarak ayarlayın:
Python
attachment
nesnesini oluştururken, courses.courseWork.addOnAttachments
uç noktasına CREATE
isteği göndermeden önce maxPoints
alanını ekleyin. Verdiğimiz örneği uygulayarak bu kimliği 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österimde, maxPoints
değerini yerel Ek veritabanınızda da depolamış olursunuz. Bu sayede, öğrenci gönderimlerine not verirken 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 GET
isteği gönderin. Bunu yaparken CourseWork.id
alanındaki itemId
öğesini iletin.
Şimdi veritabanı modelinizi, ekin 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. Verdiğimiz örneği uygulayarak bu kimliği 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 geri dönün. Yanıtta döndürülen maxPoints
değerini depolayı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()
Ekin maksimum not artık var. Bu davranışı şu anda test edebiliyor olmalısınız. Yeni bir ödeve ek ekleyin ve ek kartında "Not senkronizasyonu" etiketinin gösterildiğinden ve ödevin "Puan" değerinin değiştiğinden emin olun.
Classroom'da öğrenci gönderim notu belirleme
Bu bölümde, ek notu için payın nasıl belirleneceği açıklanmaktadır. Bu, ek için bireysel bir öğrencinin puanıdır. Bunun için öğrenci gönderiminin pointsEarned
değerini belirleyin.
Artık vermeniz gereken önemli bir karar var: Eklentiniz, pointsEarned
ayarlamak için nasıl bir istek göndermelidir?
Sorun, pointsEarned
ayarının teacher
OAuth kapsamını gerektirmesinden kaynaklanmaktadır.
Öğrenci kullanıcılarına teacher
kapsamı vermemelisiniz. Bu durum, öğrenciler eklentinizle etkileşim kurduğunda beklenmedik davranışlara (ör. Öğrenci Görünümü iframe'i yerine Öğretmen Görünümü iframe'ini yüklemek) yol açabilir. Dolayısıyla, pointsEarned
değerini ayarlamak için iki seçeneğiniz vardır:
- Giriş yapmış öğretmenin kimlik bilgilerini kullanma.
- Depolanmış (çevrimdışı) öğretmen kimlik bilgilerini kullanma.
Aşağıdaki bölümlerde, her uygulama gösterilmeden önce her bir yaklaşımın dengesi 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 üst kısmındaki SET_GRADE_WITH_LOGGED_IN_USER_CREDENTIALS
bildirimini bulun. Oturum açmış öğretmenin kimlik bilgilerini kullanarak notları geri vermek için bu değeri True
olarak ayarlayın. Öğrenci etkinliği gönderdiğinde, depolanan kimlik bilgilerini kullanarak notları geri vermek 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 yöntem, uygulamanın şimdiye kadarki geri kalanını yansıttığı ve çok az çaba gerektirdiği için oldukça sezgisel görünür.
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.
- Tüm öğrenci notlarını doldurmak için bir öğretmenin her öğrenci gönderimini açması gerekebilir.
- Classroom'un notu alması ve Classroom kullanıcı arayüzünde görünmesi arasında kısa bir gecikme olur. Gecikme genellikle beş ila on saniye olsa da 30 saniye kadar da sürebilir.
Bu faktörlerin birlikte kullanılması, öğretmenlerin sınıfın notlarını eksiksiz bir şekilde doldurmak için dikkate değer, zaman alıcı manuel çalışmalar yapması gerektiği anlamına gelir.
Bu yaklaşımı uygulamak için mevcut Öğrenci Çalışma İncelemesi rotanıza bir API çağrısı daha ekleyin.
Öğrenci gönderimini ve ek kayıtlarını aldıktan sonra öğrencinin gönderimini değerlendirin ve alınan notu kaydedin. Notu, AddOnAttachmentStudentSubmission
nesnesinin pointsEarned
alanında ayarlayın. Son olarak, istek gövdesindeki AddOnAttachmentStudentSubmission
örneğiyle courses.courseWork.addOnAttachments.studentSubmissions
uç noktasına bir PATCH
isteği gönderin. PATCH
isteğimizdeki updateMask
içinde pointsEarned
öğesini 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 notları belirleme
Notları belirlemeye yönelik ikinci yaklaşım, eki oluşturan öğretmen için depolanmış 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 bu kimlik bilgilerini kullanarak pointsEarned
ayarını yapmanız gerekir.
Bu yaklaşımın önemli bir avantajı, notların Classroom kullanıcı arayüzünde öğretmenin işlem yapmasına gerek kalmadan doldurulması ve yukarıda belirtilen sorunlardan kaçınmasıdır. 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:
- Erişim jetonu depolamak için kullanıcı veritabanı kayıtlarını değiştirme
- Ek veritabanı kayıtlarını öğretmen kimliğini depolayacak şekilde değiştirme.
- Öğretmenin kimlik bilgilerini alın ve (isteğe bağlı olarak) yeni bir Classroom hizmet örneği oluşturun.
- Gönderimin notunu belirleyebilirsiniz.
Bu gösterimin amaçları doğrultusunda, öğrenci etkinliği tamamladığında (yani öğrenci formu Öğrenci Görünümü rotasında gönderdiğinde) notu belirleyin.
Erişim jetonunu depolamak için kullanıcı veritabanı kayıtlarını değiştirme
API çağrıları yapmak için yenileme jetonu ve erişim jetonu olmak üzere iki benzersiz jeton gereklidir. Şimdiye kadar adım adım açıklamalı kılavuz serisini takip ettiyseniz User
tablo şemanızın bir yenileme jetonu depolaması gerekir. 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, artık 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 jetonunun yanı sıra depolamanı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
Sağlanan örneğimizde bu, 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()
Öğretmen kimliğini depolamak için Ek veritabanı kayıtlarını 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ınaGET
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 ekler 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ızın Attachment
tablosuna öğretmen kimliği alanı ekleyin:
Python
Sağlanan örneğimizde bu, 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 kodları güncelleyerek içerik üreticinin kimliğini de saklayın:
Python
Sağladığımız örneğimizde bu, 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 sunan 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ımdaki hazırlık düşünüldüğünde bu süreç basit olmalıdır. Bunları, öğretmen kullanıcı için Classroom hizmetinin yeni bir örneğini oluşturmak amacıyla da kullanabilirsiniz:
Python
Sağladığımız örneğimizde bu, 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,
discoveryServiceUrl=f"https://classroom.googleapis.com/$discovery/rest?labels=ADD_ONS_ALPHA&key={GOOGLE_API_KEY}",
credentials=teacher_credentials)
Gönderimin notunu belirleme
Buradaki prosedür, oturum açmış öğretmenin kimlik bilgilerini kullanma işlemiyle aynıdır. Ancak, çağrıyı önceki adımda aldığınız öğ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ü eki olan bir ödev oluşturun, öğrenci olarak yanıt gönderin ve ardından ödevini Öğrenci Çalışma İncelemesi iframe'inde açın. Uygulama yaklaşımınıza bağlı olarak notun farklı zamanlarda gösterildiğini 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ı İnceleme iframe'ini açtığında notu geri vermeyi seçtiyseniz not, iframe yüklendikten hemen sonra "Not" kutusunda görünür. Yukarıda belirtildiği gibi bu işlem 30 saniyeyi bulabilir. 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öründüğünü doğrulayın.
Tebrikler! Bir sonraki adıma geçmeye hazırsınız: Google Classroom dışında ek oluşturma.