بدء استخدام قواعد التقييم

rubric هو نموذج يمكن للمعلّمين استخدامه عند وضع الدرجات على مواد التقييم التي يقدّمها الطلاب. تتيح لك Classroom API التصرّف نيابةً عن المعلّم لإدارة هذه التصنيفات.

عرض قواعد التقييم في واجهة مستخدم Classroom الشكل 1. عرض نموذج لقواعد التقييم في مهمة على Classroom

يشرح هذا الدليل المفاهيم الأساسية ووظائف Rubrics API. يمكنك الاطّلاع على مقالات مركز المساعدة هذه للتعرّف على البنية العامة لقواعد التقييم وكيفية تطبيق تصنيف قواعد التقييم في واجهة مستخدم Classroom.

المتطلبات الأساسية

يفترض هذا الدليل حصولك على ما يلي:

مصادقة بيانات الاعتماد لتطبيق سطح المكتب

للمصادقة كمستخدم نهائي والوصول إلى بيانات المستخدمين في تطبيقك، يجب إنشاء معرّف عميل OAuth 2.0 واحد أو أكثر. يُستخدم معرّف العميل لتحديد تطبيق واحد لخوادم OAuth في Google. إذا كان تطبيقك يعمل على أنظمة أساسية متعدّدة، عليك إنشاء معرّف عميل منفصل لكل نظام أساسي.

  1. انتقِل إلى صفحة بيانات الاعتماد في Google Cloud في وحدة التحكّم في Google Cloud.
  2. انقر على إنشاء بيانات اعتماد > معرِّف عميل OAuth.
  3. انقر على نوع التطبيق > تطبيق كمبيوتر مكتبي.
  4. في حقل الاسم، اكتب اسمًا لبيانات الاعتماد. ولا يظهر هذا الاسم إلا في "وحدة تحكُّم Google Cloud". على سبيل المثال، "عميل معاينة العناوين".
  5. انقر على إنشاء. تظهر شاشة "تم إنشاء عميل OAuth"، وتعرض معرّف العميل وسر العميل الجديدَين.
  6. انقر على تنزيل ملف JSON، ثم على حسنًا. تظهر بيانات الاعتماد التي تم إنشاؤها حديثًا ضمن معرّفات عملاء OAuth 2.0.
  7. احفظ ملف JSON الذي تم تنزيله باسم credentials.json، وانقل الملف إلى دليل العمل.
  8. انقر على Create Credentials (إنشاء بيانات اعتماد) > API Key (مفتاح واجهة برمجة التطبيقات) وراجِع مفتاح واجهة برمجة التطبيقات.

اطّلِع على إنشاء بيانات اعتماد الوصول لمعرفة المزيد.

ضبط نطاقات OAuth

استنادًا إلى نطاقات OAuth الحالية لمشروعك، قد تحتاج إلى ضبط نطاقات إضافية.

  1. انتقِل إلى شاشة طلب الموافقة المتعلّقة ببروتوكول OAuth.
  2. انقر على تعديل التطبيق > حفظ ومتابعة للوصول إلى شاشة "النطاقات".
  3. انقر على إضافة نطاقات أو إزالتها.
  4. أضِف النطاقات التالية إذا لم تكن لديك:
    • https://www.googleapis.com/auth/classroom.coursework.students
    • https://www.googleapis.com/auth/classroom.courses
  5. بعد ذلك، انقر على "تعديل" > حفظ ومتابعة > حفظ ومتابعة > الرجوع إلى لوحة البيانات.

اطّلِع على مقالة ضبط شاشة طلب الموافقة المتعلّقة ببروتوكول OAuth للتعرّف على معلومات إضافية.

يتيح نطاق classroom.coursework.students إمكانية الوصول إلى قواعد التقييم للقراءة والكتابة (بالإضافة إلى الوصول إلى CourseWork)، ويسمح نطاق classroom.courses بالدورات التدريبية في القراءة والكتابة.

يتم إدراج النطاقات المطلوبة لطريقة معيّنة في المستندات المرجعية للطريقة. يمكنك الاطّلاع على courses.courseWork.rubrics.create نطاقات التفويض كمثال. يمكنك الاطّلاع على جميع نطاقات Classroom في نطاقات OAuth 2.0 لواجهات برمجة تطبيقات Google. لم تتم الإشارة إلى العناوين هنا لأنّ واجهة برمجة التطبيقات لا تزال في مرحلة المعاينة.

ضبط العيّنة

في دليل العمل، ثبِّت مكتبة عملاء Google لبرنامج Python:

pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

أنشئ ملفًا باسم main.py ينشئ مكتبة العميل ويمنح الإذن للمستخدم، باستخدام مفتاح واجهة برمجة التطبيقات بدلاً من YOUR_API_KEY:

import json
import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/classroom.courses',
          'https://www.googleapis.com/auth/classroom.coursework.students']

def build_authenticated_service(api_key):
    """Builds the Classroom service."""
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run.
        with open('token.json', 'w') as token:
            token.write(creds.to_json())

    try:
        # Build the Classroom service.
        service = build(
            serviceName="classroom",
            version="v1",
            credentials=creds,
            discoveryServiceUrl=f"https://classroom.googleapis.com/$discovery/rest?labels=DEVELOPER_PREVIEW&key={api_key}")

        return service

    except HttpError as error:
        print('An error occurred: %s' % error)

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

شغِّل النص البرمجي باستخدام python main.py. سيُطلب منك تسجيل الدخول والموافقة على نطاقات OAuth.

إنشاء مهمة دراسية

يكون مقياس التقييم مرتبطًا بمهمة أو CourseWork، ولا يكون له قيمة إلا في سياق هذا CourseWork. لا يمكن إنشاء قوائم التقييم إلا من قِبل مشروع Google Cloud الذي أنشأ العنصرCourseWork الرئيسي. لأغراض هذا الدليل، أنشئ مهمة CourseWork جديدة باستخدام نص برمجي.

أضِف ما يلي إلى main.py:

def get_latest_course(service):
    """Retrieves the last created course."""
    try:
        response = service.courses().list(pageSize=1).execute()
        courses = response.get("courses", [])
        if not courses:
            print("No courses found. Did you remember to create one in the UI?")
            return
        course = courses[0]
        return course

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

def create_coursework(service, course_id):
    """Creates and returns a sample coursework."""
    try:
        coursework = {
            "title": "Romeo and Juliet analysis.",
            "description": """Write a paper arguing that Romeo and Juliet were
                                time travelers from the future.""",
            "workType": "ASSIGNMENT",
            "state": "PUBLISHED",
        }
        coursework = service.courses().courseWork().create(
            courseId=course_id, body=coursework).execute()
        return coursework

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

عدِّل الآن main.py لاسترداد course_id لفئة الاختبار التي أنشأتها توًا، وأنشئ نموذج مهمة جديدًا، واسترِد coursework_id المهمة:

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    course = get_latest_course(service)
    course_id = course.get("id")
    course_name = course.get("name")
    print(f"'{course_name}' course ID: {course_id}")

    coursework = create_coursework(service, course_id)
    coursework_id = coursework.get("id")
    print(f"Assignment created with ID {coursework_id}")

    #TODO(developer): Save the printed course and coursework IDs.

احفظ course_id وcoursework_id. هذه السمات مطلوبة لجميع عمليات CRUD في العناوين.

من المفترض أن يكون لديك الآن نموذج "CourseWork" في Classroom.

عرض مهمة في واجهة مستخدم Classroom الشكل 2: عرض نموذج لمهمة في Classroom

إنشاء قواعد تقييم

أنت الآن جاهز لبدء إدارة العناوين الرئيسية.

يمكن إنشاء مقياس تقييم في CourseWork باستخدام طلب Create يحتوي على كائن المقياس الكامل، حيث يتم حذف سمات التعريف للمعايير والمستويات (يتم إنشاؤها عند الإنشاء).

أضِف الدالة التالية إلى main.py:

def create_rubric(service, course_id, coursework_id):
    """Creates an example rubric on a coursework."""
    try:
        body = {
            "criteria": [
                {
                    "title": "Argument",
                    "description": "How well structured your argument is.",
                    "levels": [
                        {"title": "Convincing",
                         "description": "A compelling case is made.", "points": 30},
                        {"title": "Passable",
                         "description": "Missing some evidence.", "points": 20},
                        {"title": "Needs Work",
                         "description": "Not enough strong evidence..", "points": 0},
                    ]
                },
                {
                    "title": "Spelling",
                    "description": "How well you spelled all the words.",
                    "levels": [
                        {"title": "Perfect",
                         "description": "No mistakes.", "points": 20},
                        {"title": "Great",
                         "description": "A mistake or two.", "points": 15},
                        {"title": "Needs Work",
                         "description": "Many mistakes.", "points": 5},
                    ]
                },
                {
                    "title": "Grammar",
                    "description": "How grammatically correct your sentences are.",
                    "levels": [
                        {"title": "Perfect",
                         "description": "No mistakes.", "points": 20},
                        {"title": "Great",
                         "description": "A mistake or two.", "points": 15},
                        {"title": "Needs Work",
                         "description": "Many mistakes.", "points": 5},
                    ]
                },
            ]
        }

        rubric = service.courses().courseWork().rubrics().create(
            courseId=course_id, courseWorkId=coursework_id, body=body,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
            ).execute()
        print(f"Rubric created with ID {rubric.get('id')}")
        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

بعد ذلك، عدِّل main.py وشغِّله لإنشاء مثال على مقياس التقييم، باستخدام معرّفي Course وCourseWork من الخطوات السابقة:

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    rubric = create_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
    print(json.dumps(rubric, indent=4))

إليك بعض النقاط حول تمثيل قواعد التقييم:

  • يظهر ترتيب المعايير والمستويات في واجهة مستخدم Classroom.
  • يجب ترتيب المستويات التي تم احتساب نقاط لها (تلك التي تحتوي على السمة points) حسب النقاط سواءً بترتيب تصاعدي أو تنازلي (لا يمكن ترتيبها عشوائيًا).
  • يمكن للمعلّمين إعادة ترتيب المعايير والمستويات التي تم احتساب نقاط لها (وليس المستويات التي لم يتم احتساب نقاط لها) في واجهة المستخدم، ما يؤدي إلى تغيير ترتيبها في البيانات.

راجِع القيود للاطّلاع على مزيد من التنبيهات بشأن بنية قواعد التقييم.

في واجهة المستخدم، من المفترض أن يظهر لك مقياس التقييم في المهمة.

عرض قواعد التقييم في واجهة مستخدم Classroom الشكل 3. عرض نموذج لقواعد التقييم في مهمة على Classroom

قراءة قواعد التقييم

يمكن قراءة قواعد التقييم باستخدام الطريقتَين العاديتَين List وGet.

يمكن أن يكون هناك مقياس واحد فقط في كل مهمة، لذا قد يبدو الخيار List غير واضح، ولكنه مفيد إذا لم يكن لديك رقم تعريف المقياس. في حال عدم توفّر قواعد تقييم مرتبطة بـ CourseWork، ستكون استجابة List فارغة.

أضِف الدالة التالية إلى main.py:

def get_rubric(service, course_id, coursework_id):
    """
    Get the rubric on a coursework. There can only be at most one.
    Returns null if there is no rubric.
    """
    try:
        response = service.courses().courseWork().rubrics().list(
            courseId=course_id, courseWorkId=coursework_id,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
            ).execute()

        rubrics = response.get("rubrics", [])
        if not rubrics:
            print("No rubric found for this assignment.")
            return
        rubric = rubrics[0]
        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

عدِّل main.py وشغِّله للحصول على المخطّط الذي أضفته:

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    rubric = get_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
    print(json.dumps(rubric, indent=4))

    #TODO(developer): Save the printed rubric ID.

سجِّل السمة id في نموذج التقييم للاطّلاع على الخطوات اللاحقة.

يحقّق Get أداءً جيدًا عند توفُّر رقم تعريف قاعدة التقييم. قد يبدو استخدام Get في الدالة بدلاً من ذلك على النحو التالي:

def get_rubric(service, course_id, coursework_id, rubric_id):
    """
    Get the rubric on a coursework. There can only be at most one.
    Returns a 404 if there is no rubric.
    """
    try:
        rubric = service.courses().courseWork().rubrics().get(
            courseId=course_id,
            courseWorkId=coursework_id,
            id=rubric_id,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
        ).execute()

        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

يعرض هذا التنفيذ رسالة الخطأ 404 في حال عدم توفّر مقياس.

تعديل قواعد تقييم

يتم إجراء التعديلات على مقياس التقييم من خلال مكالمات Patch. بسبب البنية المعقدة للمقياس، يجب إجراء التعديلات باستخدام نمط القراءة والتعديل والكتابة، حيث يتم استبدال سمة criteria بالكامل.

في ما يلي قواعد التعديل:

  1. تُعدّ المعايير أو المستويات المُضافة بدون رقم تعريف إضافات.
  2. إنّ المعايير أو المستويات غير المتوفّرة من قبل تُعدّ عمليات حذف.
  3. تُعتبَر المعايير أو المستويات التي تتضمّن معرّفًا حاليًا ولكنّها تتضمّن بيانات معدَّلة تعديلات. ويتم ترك السمات غير المعدَّلة كما هي.
  4. تُعتبَر المعايير أو المستويات التي يتم تقديمها باستخدام معرّفات جديدة أو غير معروفة أخطاء.
  5. يُعدّ ترتيب المعايير والمستويات الجديدة هو ترتيب واجهة المستخدم الجديدة (مع القيود المذكورة أعلاه).

أضِف دالة لتعديل قواعد التقييم:

def update_rubric(service, course_id, coursework_id, rubric_id, body):
    """
    Updates the rubric on a coursework.
    """
    try:
        rubric = service.courses().courseWork().rubrics().patch(
            courseId=course_id,
            courseWorkId=coursework_id,
            id=rubric_id,
            body=body,
            updateMask='criteria',
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
        ).execute()

        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

في هذا المثال، تم تحديد الحقل criteria لتعديله باستخدام updateMask.

بعد ذلك، عدِّل main.py لإجراء تغيير في كلّ من قواعد التحديث المذكورة أعلاه:

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    # Get the latest rubric.
    rubric = get_rubric(service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
    criteria = rubric.get("criteria")
    """
    The "criteria" property should look like this:
    [
        {
            "id": "NkEyMdMyMzM2Nxkw",
            "title": "Argument",
            "description": "How well structured your argument is.",
            "levels": [
                {
                    "id": "NkEyMdMyMzM2Nxkx",
                    "title": "Convincing",
                    "description": "A compelling case is made.",
                    "points": 30
                },
                {
                    "id": "NkEyMdMyMzM2Nxky",
                    "title": "Passable",
                    "description": "Missing some evidence.",
                    "points": 20
                },
                {
                    "id": "NkEyMdMyMzM2Nxkz",
                    "title": "Needs Work",
                    "description": "Not enough strong evidence..",
                    "points": 0
                }
            ]
        },
        {
            "id": "NkEyMdMyMzM2Nxk0",
            "title": "Spelling",
            "description": "How well you spelled all the words.",
            "levels": [...]
        },
        {
            "id": "NkEyMdMyMzM2Nxk4",
            "title": "Grammar",
            "description": "How grammatically correct your sentences are.",
            "levels": [...]
        }
    ]
    """

    # Make edits. This example will make one of each type of change.

    # Add a new level to the first criteria. Levels must remain sorted by
    # points.
    new_level = {
        "title": "Profound",
        "description": "Truly unique insight.",
        "points": 50
    }
    criteria[0]["levels"].insert(0, new_level)

    # Remove the last criteria.
    del criteria[-1]

    # Update the criteria titles with numeric prefixes.
    for index, criterion in enumerate(criteria):
        criterion["title"] = f"{index}: {criterion['title']}"

    # Resort the levels from descending to ascending points.
    for criterion in criteria:
        criterion["levels"].sort(key=lambda level: level["points"])

    # Update the rubric with a patch call.
    new_rubric = update_rubric(
        service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID, YOUR_RUBRIC_ID, rubric)

    print(json.dumps(new_rubric, indent=4))

يجب أن تظهر التغييرات الآن للمعلّم في Classroom.

عرض قاعدة تقييم معدَّلة في واجهة مستخدم Classroom الشكل 4. عرض قاعدة التقييم المعدّلة

عرض التقييمات المُرسَلة استنادًا إلى قواعد التقييم

في الوقت الحالي، لا يمكن استخدام واجهة برمجة التطبيقات لمنح درجات للطلاب استنادًا إلى قواعد التقييم، ولكن يمكنك قراءة درجات قواعد التقييم للملفات التي تم منحها درجات استنادًا إلى قواعد التقييم في واجهة مستخدم Classroom.

بصفتك طالبًا في واجهة مستخدم Classroom، أكمِل نموذج المهمة الدراسية وأرسِله. بعد ذلك، بصفتك معلِّمًا، يمكنك وضع الدرجات على المهمة الدراسية يدويًا باستخدام نموذج التقييم.

عرض درجة قواعد وضع العلامات في واجهة مستخدم Classroom الشكل 5: طريقة عرض المعلّم لقواعد التقييم أثناء وضع الدرجات

إنّ الأعمال التي أرسلها الطلاب والتي تم وضع علامات عليها باستخدام مقياس تقييم تتضمّن سمتَين جديدتَين: draftRubricGrades وassignedRubricGrades، اللتان تمثّلان النقاط والمستويات التي اختارها المعلّم أثناء مرحلة المسودة وحالات وضع العلامات المخصّصة، على التوالي.

بالإضافة إلى ذلك، تحتوي الواجبات التي يرسلها الطلاب والتي تتضمّن قواعد تقييم مرتبطة بها على حقل rubricId ، حتى قبل وضع الدرجات. يمثّل هذا الحقل أحدث مقياس مرتبط بالتقييمCourseWork، وقد تتغيّر هذه القيمة إذا حذف المعلّمون مقياساً وأعدّوا إنشاءه.

يمكنك استخدام الطريقتَين الحاليتَين studentSubmissions.Get و studentSubmissions.List لعرض المراجعات التي تم تقييمها.

أضِف الدالة التالية إلى main.py لعرض المهام التي أرسلها الطلاب:

def get_latest_submission(service, course_id, coursework_id):
    """Retrieves the last submission for an assignment."""
    try:
        response = service.courses().courseWork().studentSubmissions().list(
            courseId = course_id,
            courseWorkId = coursework_id,
            pageSize=1,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
        ).execute()
        submissions = response.get("studentSubmissions", [])
        if not submissions:
            print(
                """No submissions found. Did you remember to turn in and grade
                   the assignment in the UI?""")
            return
        submission = submissions[0]
        return submission

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

بعد ذلك، عدِّل main.py وشغِّله للاطّلاع على درجات الإرسال.

if __name__ == '__main__':
    service = build_authenticated_service(YOUR_API_KEY)

    submission = get_latest_submission(
        service, YOUR_COURSE_ID, YOUR_COURSEWORK_ID)
    print(json.dumps(submission, indent=4))

يتضمّن draftRubricGrades وassignedRubricGrades ما يلي:

  • criterionId لمعايير قواعد التقييم المقابلة.
  • points التي حدّدها المعلّم لكل معيار قد يرجع ذلك إلى المستوى المحدّد، ولكن من المحتمل أيضًا أن يكون المعلّم قد استبدل هذا الإعداد.
  • levelId من المستوى الذي تم اختياره لكل معيار. إذا لم يحدد المعلّم مستوى، ولكنّه منح نقاطًا للمعيار، لن يظهر هذا الحقل.

لا تحتوي هذه القوائم إلا على إدخالات للمعايير التي إما أن يحدد فيها المعلّم مستوى أو يحدّد نقاطًا. على سبيل المثال، إذا اختار المعلّم التعامل مع معيار واحد فقط أثناء التقييم، لن يتضمّن draftRubricGrades وassignedRubricGrades سوى عنصر واحد، حتى إذا كانت التقييمات تحتوي على العديد من المعايير.

حذف قواعد تقييم

يمكن حذف قواعد التقييم من خلال طلب Delete عادي. يعرض الرمز البرمجي التالي مثالاً على دالة الاكتمال، ولكن بما أنّ وضع الدرجات قد بدأ بالفعل، لا يمكنك حذف نموذج التقييم الحالي:

def delete_rubric(service, course_id, coursework_id, rubric_id):
    """Deletes the rubric on a coursework."""
    try:
        service.courses().courseWork().rubrics().delete(
            courseId=course_id,
            courseWorkId=coursework_id,
            id=rubric_id,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
        ).execute()

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

تصدير قواعد التقييم واستيرادها

يمكن تصدير قواعد التقييم يدويًا إلى "جداول بيانات Google" لإعادة استخدامها من قِبل المعلّمين.

بالإضافة إلى تحديد معايير العناوين في الرمز، من الممكن إنشاء عناوين و تعديلها من هذه الجداول التي تم تصديرها عن طريق تحديد القيمة sourceSpreadsheetId في نص العنوان بدلاً من criteria:

def create_rubric_from_sheet(service, course_id, coursework_id, sheet_id):
    """Creates an example rubric on a coursework."""
    try:
        body = {
            "sourceSpreadsheetId": sheet_id
        }

        rubric = service.courses().courseWork().rubrics().create(
            courseId=course_id, courseWorkId=coursework_id, body=body,
            # Specify the preview version. Rubrics CRUD capabilities are
            # supported in V1_20231110_PREVIEW and later.
            previewVersion="V1_20231110_PREVIEW"
            ).execute()

        print(f"Rubric created with ID {rubric.get('id')}")
        return rubric

    except HttpError as error:
        print(f"An error occurred: {error}")
        return error

ملاحظات

إذا واجهت أي مشاكل أو كان لديك ملاحظات، يُرجى مشاركة ملاحظاتك.