Learning Metrics Dashboard

L3
ModelContextProtocolNotionPython Roadmap

Create a comprehensive Learning Metrics Dashboard section displaying precise statistics and recommendations based on the Steps database.

Created by Lingjun Chen
2025-08-02
Data AggregationConditional FilteringReport GenerationVisual Formatting

Model Ranking

Click on the dots to view the trajectory of each task run
Model
Run Results
Pass@4
Pass^4
Avg Time
Avg Turns
Input Tokens
Output Tokens
Total Tokens
Claude
claude-opus-4-1
0
/1
--
272.9s
11.0
364,570
4,207
368,777
Claude
claude-sonnet-4
0
/4
131.9s
12.3
430,278
3,600
433,879
Claude
claude-sonnet-4-high
0
/4
118.3s
11.3
756,682
3,400
760,081
Claude
claude-sonnet-4-low
0
/4
139.0s
12.8
811,717
4,526
816,243
DeepSeek
deepseek-chat
0
/4
197.1s
10.5
344,350
2,260
346,610
Gemini
gemini-2-5-flash
0
/4
124.7s
6.8
300,653
24,305
324,958
Gemini
gemini-2-5-pro
0
/4
195.9s
7.5
267,020
17,764
284,784
Z.ai
glm-4-5
0
/4
222.6s
15.3
396,899
5,613
402,512
OpenAI
gpt-4-1
0
/4
25.7s
5.8
87,756
626
88,381
OpenAI
gpt-4-1-mini
0
/4
50.4s
7.5
242,408
1,403
243,811
OpenAI
gpt-4-1-nano
0
/4
69.1s
9.8
52,000
8,982
60,982
OpenAI
gpt-5-high
0
/4
1610.0s
13.8
377,330
76,664
453,994
OpenAI
gpt-5-low
0
/4
843.8s
12.8
529,015
51,128
580,144
OpenAI
gpt-5-medium
0
/4
1035.1s
13.0
526,007
53,698
579,706
OpenAI
gpt-5-mini-high
0
/4
978.7s
18.8
1,740,461
86,519
1,826,979
OpenAI
gpt-5-mini-low
0
/4
94.3s
7.8
381,035
7,769
388,803
OpenAI
gpt-5-mini-medium
0
/4
259.5s
12.5
877,482
21,425
898,907
OpenAI
gpt-5-nano-high
0
/4
201.8s
4.3
176,659
39,032
215,691
OpenAI
gpt-5-nano-low
0
/4
31.0s
1.5
12,438
5,839
18,277
OpenAI
gpt-5-nano-medium
0
/4
100.2s
3.8
121,473
16,998
138,470
OpenAI
gpt-oss-120b
0
/4
13.0s
3.5
27,211
598
27,809
Grok
grok-4
0
/4
649.0s
23.5
1,078,861
12,405
1,091,266
Grok
grok-code-fast-1
0
/4
250.2s
24.0
731,959
6,967
741,144
MoonshotAI
kimi-k2-0711
0
/4
107.4s
11.8
309,737
2,473
312,210
MoonshotAI
kimi-k2-0905
0
/4
876.6s
46.3
2,325,116
10,484
2,335,600
OpenAI
o3
0
/4
273.8s
8.5
177,491
15,773
193,264
OpenAI
o4-mini
0
/4
486.8s
6.8
110,526
21,093
131,619
Qwen
qwen-3-coder-plus
0
/4
112.8s
20.0
1,004,570
2,853
1,007,423
Qwen
qwen-3-max
0
/4
140.3s
14.0
628,474
3,830
632,304

Task State

Notion Workspace
This task is executed based on this Notion workspace
This workspace is cloned from notion official template marketplace.View Original Template

Instruction

Task: Learning Metrics Dashboard

Objective

Create a comprehensive Learning Metrics Dashboard section in the Python Roadmap page that displays precise statistics and recommendations based on the Steps database content.

Requirements

1. Section Placement

  • Add new content immediately after the Learning Materials section (before Whether you're starting from scratch or).

2. Dashboard Header

  • Type: heading_3
  • Text: 📊 Learning Metrics Dashboard

3. Course Statistics Block

  • Type: callout
  • Background Color: Brown
  • Icon: None
  • Title: Course Statistics (bold, heading_3). Use the same color scheme as other callout headings.
  • Content: Bulleted list with the following items in exact order:
    • Total Lessons: [X] (count all entries in Steps database)
    • Completed: [X] ([Y]%) (count Status="Done", calculate percentage to 1 decimal)
    • In Progress: [X] ([Y]%) (count Status="In Progress", calculate percentage to 1 decimal)
    • Beginner Level: [X] lessons ([Y] completed) (filter by Chapters relation to Beginner Level)
    • Intermediate Level: [X] lessons ([Y] completed) (filter by Chapters relation to Intermediate Level)
    • Advanced Level: [X] lessons ([Y] completed) (filter by Chapters relation to Advanced Level)

4. Completed Topics Section

  • Type: toggle
  • Text: 🏆 Completed Topics (Click to expand)
  • Nested Content: Numbered list containing exactly 5 items
    • List lessons with Status="Done"


Verify

*.py
Python
import sys
from notion_client import Client
from tasks.utils import notion_utils

def get_page_title_from_result(page_result):
    """
    Extract the title from a page result object from database query.
    """
    properties = page_result.get('properties', {})
    # Try common title property names
    for prop_name in ['Name', 'Title', 'title', 'Lessons']:
        if prop_name in properties:
            prop = properties[prop_name]
            if prop.get('type') == 'title':
                title_array = prop.get('title', [])
                if title_array and len(title_array) > 0:
                    return title_array[0].get('plain_text', '')
    return ''

def verify(notion: Client, main_id: str = None) -> bool:
    """
    Verifies that the Learning Metrics Dashboard has been implemented correctly according to description.md.
    """
    # Step 1: Find the main page and get all blocks
    if main_id:
        found_id, object_type = notion_utils.find_page_or_database_by_id(notion, main_id)
        if not found_id or object_type != 'page':
            print("Error: Main page not found.", file=sys.stderr)
            return False
    else:
        # Try to find the main page by searching
        found_id = notion_utils.find_page(notion, "Python Roadmap")
        if not found_id:
            print("Error: Main page not found.", file=sys.stderr)
            return False
    
    print(f"Found main page: {found_id}")
    
    # Get Steps database to calculate expected statistics
    steps_db_id = notion_utils.find_database(notion, "Steps")
    if not steps_db_id:
        print("Error: Steps database not found.", file=sys.stderr)
        return False
    
    # Query Steps database to get all lessons
    steps_data = notion.databases.query(database_id=steps_db_id)
    total_lessons = len(steps_data['results'])
    completed_count = 0
    in_progress_count = 0
    completed_lessons = []
    
    # Get Chapters database for level information
    chapters_db_id = notion_utils.find_database(notion, "Chapters")
    if not chapters_db_id:
        print("Error: Chapters database not found.", file=sys.stderr)
        return False
    
    # Query Chapters database to get level information
    chapters_data = notion.databases.query(database_id=chapters_db_id)
    level_ids = {
        'Beginner Level': None,
        'Intermediate Level': None,
        'Advanced Level': None
    }
    
    for chapter in chapters_data['results']:
        chapter_name = get_page_title_from_result(chapter)
        if chapter_name in level_ids:
            level_ids[chapter_name] = chapter['id']
    
    # Initialize level counts
    level_counts = {
        'Beginner Level': {'total': 0, 'completed': 0},
        'Intermediate Level': {'total': 0, 'completed': 0},
        'Advanced Level': {'total': 0, 'completed': 0}
    }
    
    # Count lessons by status and level
    for lesson in steps_data['results']:
        status = lesson['properties']['Status']['status']
        if status and status['name'] == 'Done':
            completed_count += 1
            lesson_title = get_page_title_from_result(lesson)
            if lesson_title:
                completed_lessons.append(lesson_title)
        elif status and status['name'] == 'In Progress':
            in_progress_count += 1
        
        # Count by level
        chapters_relation = lesson['properties']['Chapters']['relation']
        for chapter_ref in chapters_relation:
            chapter_id = chapter_ref['id']
            for level_name, level_id in level_ids.items():
                if chapter_id == level_id:
                    level_counts[level_name]['total'] += 1
                    if status and status['name'] == 'Done':
                        level_counts[level_name]['completed'] += 1
    
    # Calculate percentages
    completed_percentage = round((completed_count / total_lessons * 100), 1) if total_lessons > 0 else 0
    in_progress_percentage = round((in_progress_count / total_lessons * 100), 1) if total_lessons > 0 else 0
    
    print(f"Expected statistics:")
    print(f"  Total Lessons: {total_lessons}")
    print(f"  Completed: {completed_count} ({completed_percentage}%)")
    print(f"  In Progress: {in_progress_count} ({in_progress_percentage}%)")
    print(f"  Beginner Level: {level_counts['Beginner Level']['total']} lessons ({level_counts['Beginner Level']['completed']} completed)")
    print(f"  Intermediate Level: {level_counts['Intermediate Level']['total']} lessons ({level_counts['Intermediate Level']['completed']} completed)")
    print(f"  Advanced Level: {level_counts['Advanced Level']['total']} lessons ({level_counts['Advanced Level']['completed']} completed)")
    print(f"  Completed lessons (first 5): {completed_lessons[:5]}")
    
    # Get all blocks from the page
    all_blocks = notion_utils.get_all_blocks_recursively(notion, found_id)
    print(f"Found {len(all_blocks)} blocks")
    
    # Step 2: Verify the required elements in order
    learning_materials_idx = -1
    dashboard_heading_idx = -1
    callout_idx = -1
    toggle_idx = -1
    whether_paragraph_idx = -1  # Track the "Whether you're starting from scratch" paragraph
    
    # Track what we've verified
    callout_has_brown_bg = False
    callout_has_no_icon = False
    callout_has_course_statistics_title = False
    callout_title_has_correct_colors = False
    statistics_items_found = []
    completed_topics_found = []
    
    # Expected statistics content
    expected_statistics = [
        f"Total Lessons: {total_lessons}",
        f"Completed: {completed_count} ({completed_percentage}%)",
        f"In Progress: {in_progress_count} ({in_progress_percentage}%)",
        f"Beginner Level: {level_counts['Beginner Level']['total']} lessons ({level_counts['Beginner Level']['completed']} completed)",
        f"Intermediate Level: {level_counts['Intermediate Level']['total']} lessons ({level_counts['Intermediate Level']['completed']} completed)",
        f"Advanced Level: {level_counts['Advanced Level']['total']} lessons ({level_counts['Advanced Level']['completed']} completed)"
    ]
    
    # Check blocks in order
    for i, block in enumerate(all_blocks):
        if block is None:
            continue
            
        block_type = block.get("type")
        
        # 1. Check for Learning Materials heading (requirement 1)
        if learning_materials_idx == -1 and block_type == "heading_3":
            block_text = notion_utils.get_block_plain_text(block)
            if "🎓 Learning Materials" in block_text or "Learning Materials" in block_text:
                learning_materials_idx = i
                print(f"✓ Requirement 1: Found Learning Materials heading at position {i}")
        
        # 2. Check for Learning Metrics Dashboard heading after Learning Materials (requirement 2)
        elif learning_materials_idx != -1 and dashboard_heading_idx == -1 and block_type == "heading_3":
            block_text = notion_utils.get_block_plain_text(block)
            if "📊 Learning Metrics Dashboard" in block_text:
                dashboard_heading_idx = i
                print(f"✓ Requirement 2: Found Learning Metrics Dashboard heading at position {i}")
        
        # 3. Check for callout block after Dashboard heading (requirement 3)
        elif dashboard_heading_idx != -1 and callout_idx == -1 and block_type == "callout":
            callout_idx = i
            print(f"  Found callout block at position {i}")
            
            # Check brown background (requirement 3.1)
            if block.get("callout", {}).get("color") == "brown_background":
                callout_has_brown_bg = True
                print(f"  ✓ Requirement 3.1: Callout has brown background")
            
            # Check no icon (requirement 3.2)
            icon = block.get("callout", {}).get("icon")
            if icon is None:
                callout_has_no_icon = True
                print(f"  ✓ Requirement 3.2: Callout has no icon")
            
            # Get nested blocks for Course Statistics title and content
            nested_blocks = notion_utils.get_all_blocks_recursively(notion, block.get("id"))
            
            for nested in nested_blocks:
                # Check for heading_3 only as per requirement
                if nested and nested.get("type") == "heading_3":
                    # Check for "Course Statistics" title with correct formatting
                    rich_text = nested.get("heading_3", {}).get("rich_text", [])
                    course_found = False
                    course_correct = False
                    statistics_found = False
                    statistics_correct = False
                    
                    for text_item in rich_text:
                        text_content = text_item.get("text", {}).get("content", "")
                        annotations = text_item.get("annotations", {})
                        color = annotations.get("color", "default")
                        is_bold = annotations.get("bold", False)
                        
                        if "Course" in text_content:
                            course_found = True
                            # Check if Course is blue and bold
                            if color == "blue" and is_bold:
                                course_correct = True
                                print(f"  ✓ 'Course' has blue color and is bold")
                            else:
                                print(f"  ✗ 'Course' color: {color}, bold: {is_bold} (should be blue and bold)")
                            
                        if "Statistics" in text_content:
                            statistics_found = True
                            # Check if Statistics is yellow and bold
                            if color == "yellow" and is_bold:
                                statistics_correct = True
                                print(f"  ✓ 'Statistics' has yellow color and is bold")
                            else:
                                print(f"  ✗ 'Statistics' color: {color}, bold: {is_bold} (should be yellow and bold)")
                    
                    if course_found and statistics_found:
                        callout_has_course_statistics_title = True
                        if course_correct and statistics_correct:
                            callout_title_has_correct_colors = True
                            print(f"  ✓ Requirement 3.3: Callout has 'Course Statistics' title with correct colors")
                        else:
                            print(f"  ✗ Requirement 3.3: Title found but colors/formatting incorrect")
                
                # Check for statistics items in bulleted list
                elif nested and nested.get("type") == "bulleted_list_item":
                    item_text = notion_utils.get_block_plain_text(nested)
                    for expected_item in expected_statistics:
                        if expected_item in item_text:
                            if expected_item not in statistics_items_found:
                                statistics_items_found.append(expected_item)
                                print(f"  ✓ Requirement 3.4: Found statistics item: {expected_item}")
        
        # 4. Check for Completed Topics toggle after callout (requirement 4)
        elif callout_idx != -1 and toggle_idx == -1 and block_type == "toggle":
            block_text = notion_utils.get_block_plain_text(block)
            if "🏆 Completed Topics (Click to expand)" in block_text:
                toggle_idx = i
                print(f"✓ Requirement 4: Found Completed Topics toggle at position {i}")
                
                # Get nested blocks for completed topics list
                nested_blocks = notion_utils.get_all_blocks_recursively(notion, block.get("id"))
                for nested in nested_blocks:
                    if nested and nested.get("type") == "numbered_list_item":
                        item_text = notion_utils.get_block_plain_text(nested)
                        if item_text and item_text in completed_lessons:
                            completed_topics_found.append(item_text)
                            print(f"  ✓ Requirement 4.1: Found completed topic: {item_text}")
        
        # 5. Check for "Whether you're starting from scratch" paragraph (should be after dashboard content)
        elif block_type == "paragraph" and whether_paragraph_idx == -1:
            block_text = notion_utils.get_block_plain_text(block)
            if "Whether you're starting from scratch" in block_text or "Whether you're starting from scratch" in block_text:
                whether_paragraph_idx = i
                print(f"  Found 'Whether you're starting from scratch' paragraph at position {i}")
    
    # Step 3: Verify all requirements were met
    print(f"\nVerification Summary:")
    
    all_passed = True
    
    # Requirement 1: Learning Materials section found
    if learning_materials_idx == -1:
        print("✗ Requirement 1: Learning Materials section NOT found", file=sys.stderr)
        all_passed = False
    else:
        print("✓ Requirement 1: Learning Materials section found")
    
    # Requirement 2: Learning Metrics Dashboard heading after Learning Materials and before "Whether..." paragraph
    if dashboard_heading_idx == -1:
        print("✗ Requirement 2: Learning Metrics Dashboard heading NOT found", file=sys.stderr)
        all_passed = False
    elif dashboard_heading_idx <= learning_materials_idx:
        print("✗ Requirement 2: Learning Metrics Dashboard heading not AFTER Learning Materials", file=sys.stderr)
        all_passed = False
    elif whether_paragraph_idx != -1 and dashboard_heading_idx >= whether_paragraph_idx:
        print("✗ Requirement 2: Learning Metrics Dashboard heading not BEFORE 'Whether you're starting from scratch' paragraph", file=sys.stderr)
        all_passed = False
    else:
        print("✓ Requirement 2: Learning Metrics Dashboard heading found after Learning Materials")
        if whether_paragraph_idx != -1:
            print("  ✓ Dashboard content is correctly placed before 'Whether you're starting from scratch' paragraph")
    
    # Requirement 3: Course Statistics callout block with all specifications
    if callout_idx == -1:
        print("✗ Requirement 3: Course Statistics callout block NOT found", file=sys.stderr)
        all_passed = False
    else:
        if not callout_has_brown_bg:
            print("✗ Requirement 3.1: Callout does NOT have brown background", file=sys.stderr)
            all_passed = False
        else:
            print("✓ Requirement 3.1: Callout has brown background")
            
        if not callout_has_no_icon:
            print("✗ Requirement 3.2: Callout has an icon (should have none)", file=sys.stderr)
            all_passed = False
        else:
            print("✓ Requirement 3.2: Callout has no icon")
            
        if not callout_has_course_statistics_title:
            print("✗ Requirement 3.3: Callout does NOT have 'Course Statistics' title", file=sys.stderr)
            all_passed = False
        else:
            print("✓ Requirement 3.3: Callout has 'Course Statistics' title")
        
        if not callout_title_has_correct_colors:
            print("✗ Requirement 3.3.1: Title does NOT have correct colors (blue for Course, yellow for Statistics)", file=sys.stderr)
            all_passed = False
        else:
            print("✓ Requirement 3.3.1: Title has correct colors")
        
        # Check all statistics items
        missing_items = [item for item in expected_statistics if item not in statistics_items_found]
        if missing_items:
            print(f"✗ Requirement 3.4: Missing statistics items: {missing_items}", file=sys.stderr)
            all_passed = False
        else:
            print("✓ Requirement 3.4: All 6 statistics items found")
    
    # Requirement 4: Completed Topics toggle
    if toggle_idx == -1:
        print("✗ Requirement 4: Completed Topics toggle NOT found", file=sys.stderr)
        all_passed = False
    elif toggle_idx <= callout_idx:
        print("✗ Requirement 4: Completed Topics toggle not AFTER callout", file=sys.stderr)
        all_passed = False
    else:
        print("✓ Requirement 4: Completed Topics toggle found after callout")
        
        # Check that exactly 5 completed topics are listed
        if len(completed_topics_found) != 5:
            if len(completed_topics_found) < 5:
                print(f"✗ Requirement 4.1: Only {len(completed_topics_found)} completed topics found (need exactly 5)", file=sys.stderr)
            else:
                print(f"✗ Requirement 4.1: Found {len(completed_topics_found)} completed topics (need exactly 5, not more)", file=sys.stderr)
            all_passed = False
        else:
            print(f"✓ Requirement 4.1: Found exactly 5 completed topics as required")
    
    # Requirement 5: Proper integration (implicitly checked by order)
    if all_passed:
        print("✓ Requirement 5: All content properly integrated in correct order")
    
    return all_passed

def main():
    """
    Executes the verification process and exits with a status code.
    """
    notion = notion_utils.get_notion_client()
    main_id = sys.argv[1] if len(sys.argv) > 1 else None
    
    if verify(notion, main_id):
        print("Verification passed")
        sys.exit(0)
    else:
        print("Verification failed")
        sys.exit(1)

if __name__ == "__main__":
    main()