Layout Adjustment

L3
ModelContextProtocolNotionOnline Resume

This task involves modifying the layout and content of an online resume page by restructuring the Skills section with icon indicators and adjusting the Work History and Education sections to use equal column widths with placeholder images.

Created by Xiangyan Liu
2025-08-14
Content OrganizationVisual FormattingConditional FilteringTemplate Population

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
--
425.4s
40.0
3,192,034
6,401
3,198,435
Claude
claude-sonnet-4
0
/4
229.5s
30.8
1,166,533
4,860
1,171,392
Claude
claude-sonnet-4-high
0
/4
262.0s
34.5
1,361,800
5,308
1,367,107
Claude
claude-sonnet-4-low
0
/4
236.1s
31.8
1,214,918
5,017
1,219,935
DeepSeek
deepseek-chat
0
/4
492.6s
43.8
1,570,832
3,520
1,574,352
Gemini
gemini-2-5-flash
0
/4
48.4s
8.0
81,979
6,065
88,044
Gemini
gemini-2-5-pro
0
/4
97.2s
11.0
256,782
4,559
261,341
Z.ai
glm-4-5
0
/4
333.7s
34.5
1,081,978
5,781
1,087,759
OpenAI
gpt-4-1
0
/4
49.1s
9.0
135,919
1,336
137,255
OpenAI
gpt-4-1-mini
0
/4
38.5s
12.3
260,842
739
261,581
OpenAI
gpt-4-1-nano
0
/4
35.4s
12.0
128,379
614
128,992
OpenAI
gpt-5-high
0
/4
1124.0s
18.5
387,856
58,009
445,866
OpenAI
gpt-5-low
0
/4
765.5s
20.8
484,353
46,861
531,214
OpenAI
gpt-5-medium
0
/4
1096.7s
18.0
400,378
46,127
446,505
OpenAI
gpt-5-mini-high
0
/4
1030.4s
40.5
3,228,992
92,018
3,321,010
OpenAI
gpt-5-mini-low
0
/4
68.8s
12.0
188,239
3,431
191,669
OpenAI
gpt-5-mini-medium
0
/4
179.2s
15.3
410,922
16,371
427,293
OpenAI
gpt-5-nano-high
0
/4
321.1s
12.3
289,453
64,169
353,622
OpenAI
gpt-5-nano-low
0
/4
45.8s
2.8
15,517
8,682
24,199
OpenAI
gpt-5-nano-medium
0
/4
237.6s
12.8
154,980
44,408
199,388
OpenAI
gpt-oss-120b
0
/4
13.7s
3.5
18,125
501
18,625
Grok
grok-4
0
/4
1134.5s
23.8
932,723
15,242
947,965
Grok
grok-code-fast-1
0
/4
280.6s
27.8
897,373
7,388
909,898
MoonshotAI
kimi-k2-0711
0
/4
195.5s
28.8
786,635
2,516
789,151
MoonshotAI
kimi-k2-0905
0
/4
852.6s
54.0
2,060,114
10,431
2,070,544
OpenAI
o3
0
/4
273.0s
21.5
476,637
15,031
491,668
OpenAI
o4-mini
0
/4
484.0s
17.0
287,497
34,244
321,740
Qwen
qwen-3-coder-plus
0
/4
119.6s
31.8
1,364,725
3,441
1,368,166
Qwen
qwen-3-max
0
/4
148.2s
27.5
748,100
2,283
750,383

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

Please go to my Online Resume page and adjust the Skills display with the following requirements:

Skills Section Adjustment

  1. Delete the Skills database from the right side of the page
  2. Add a new Skills section on the left side, under the Languages section
  3. Format skills as "[icon] skill description (type)", for example "✨✨ Photoshop (Design Tool)"
    • Use ✨✨ icon for skills with level >= 50%
    • Use ✨ icon for skills with level < 50%

Work History and Education Layout Adjustment

  1. Adjust the layout so that logo/image columns take up 50% width in each section
    • Note: Column width ratio might not be returned by API when columns are equal (50/50)
  2. Replace all images/icons with black placeholder images using URL containing "https://singlecolorimage.com/get/000000/1024x128"


Verify

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


def verify(notion: Client, main_id: str = None) -> bool:
    """
    Verifies that the Skills display has been adjusted correctly:
    1. Skills database on the right side should be deleted
    2. Skills section should be added on the left side under Languages
    3. Skills should be formatted with correct icons based on skill level
    4. Work History and Education sections should use black placeholder images
    """
    page_id = None
    if main_id:
        found_id, object_type = notion_utils.find_page_or_database_by_id(
            notion, main_id
        )
        if found_id and object_type == "page":
            page_id = found_id

    if not page_id:
        page_id = notion_utils.find_page(notion, "Online Resume")
    if not page_id:
        print("Error: Page 'Online Resume' not found.", file=sys.stderr)
        return False

    all_blocks = notion_utils.get_all_blocks_recursively(notion, page_id)

    # Step 1: Verify Skills database is NOT in the right column anymore
    # Find the main column list
    for block in all_blocks:
        if block.get("type") == "column_list":
            column_list_id = block["id"]
            columns = notion_utils.get_all_blocks_recursively(notion, column_list_id)
            
            # Check if this is the main two-column layout
            if len(columns) == 2:
                # Find the right column (usually the one with larger width ratio)
                for column in columns:
                    if column.get("type") == "column":
                        width_ratio = column.get("column", {}).get("width_ratio", 0)
                        # Right column typically has width_ratio > 0.5
                        if width_ratio > 0.5:
                            right_column_id = column["id"]
                            right_column_blocks = notion_utils.get_all_blocks_recursively(
                                notion, right_column_id
                            )
                            
                            # Check if Skills database exists in right column
                            for right_block in right_column_blocks:
                                if (
                                    right_block.get("type") == "child_database"
                                    and right_block.get("child_database", {}).get("title") == "Skills"
                                ):
                                    print(
                                        "Error: Skills database still exists in the right column.",
                                        file=sys.stderr,
                                    )
                                    return False

    # Step 2: Find the left column and verify Skills section exists there
    skills_section_found = False
    skills_with_double_sparkles = []
    skills_with_single_sparkle = []
    
    # First, find the main column_list (top-level)
    main_column_list_id = None
    for block in all_blocks:
        if block.get("type") == "column_list" and block.get("parent", {}).get("type") == "page_id":
            main_column_list_id = block["id"]
            break
    
    if not main_column_list_id:
        print("Error: Main column list not found.", file=sys.stderr)
        return False
    
    # Get the columns directly
    columns = notion_utils.get_all_blocks_recursively(notion, main_column_list_id)
    
    # Find the left column (the one with width_ratio around 0.25)
    left_column_id = None
    for column in columns:
        if column.get("type") == "column":
            width_ratio = column.get("column", {}).get("width_ratio", 0)
            # Left column has width_ratio around 0.25
            if 0.2 <= width_ratio <= 0.3:
                left_column_id = column["id"]
                break
    
    if not left_column_id:
        print("Error: Left column not found.", file=sys.stderr)
        return False
    
    # Get all blocks in the left column
    left_column_blocks = notion_utils.get_all_blocks_recursively(notion, left_column_id)
    
    # Find Languages heading
    languages_index = -1
    for i, left_block in enumerate(left_column_blocks):
        if (
            left_block.get("type") == "heading_2"
            and "Languages" in notion_utils.get_block_plain_text(left_block)
        ):
            languages_index = i
            break
    
    if languages_index == -1:
        print("Error: Languages heading not found in left column.", file=sys.stderr)
        return False
    
    # Look for Skills heading after Languages
    for i in range(languages_index + 1, len(left_column_blocks)):
        left_block = left_column_blocks[i]
        
        if (
            left_block.get("type") == "heading_2"
            and "Skills" in notion_utils.get_block_plain_text(left_block)
        ):
            skills_section_found = True
            
            # Check divider after Skills heading
            if i + 1 < len(left_column_blocks):
                next_block = left_column_blocks[i + 1]
                if next_block.get("type") != "divider":
                    print(
                        "Error: Divider not found after Skills heading.",
                        file=sys.stderr,
                    )
                    return False
            
            # Collect skills after divider
            for j in range(i + 2, len(left_column_blocks)):
                skill_block = left_column_blocks[j]
                if skill_block.get("type") == "paragraph":
                    skill_text = notion_utils.get_block_plain_text(skill_block)
                    if skill_text and skill_text.strip():  # Check for non-empty text
                        # Check if text is bold
                        rich_text = skill_block.get("paragraph", {}).get("rich_text", [])
                        if rich_text and not rich_text[0].get("annotations", {}).get("bold"):
                            print(
                                f"Error: Skill '{skill_text}' is not bold.",
                                file=sys.stderr,
                            )
                            return False
                        
                        # Check icon format
                        if skill_text.startswith("✨✨"):
                            skills_with_double_sparkles.append(skill_text)
                        elif skill_text.startswith("✨"):
                            skills_with_single_sparkle.append(skill_text)
                        else:
                            print(
                                f"Error: Skill '{skill_text}' doesn't start with sparkle icon.",
                                file=sys.stderr,
                            )
                            return False
                        
                        # Check format includes type in parentheses
                        if "(" not in skill_text or ")" not in skill_text:
                            print(
                                f"Error: Skill '{skill_text}' doesn't include type in parentheses.",
                                file=sys.stderr,
                            )
                            return False
                elif skill_block.get("type") in ["heading_1", "heading_2", "heading_3"]:
                    # Stop when we reach another section
                    break
            break

    if not skills_section_found:
        print(
            "Error: Skills section not found in the left column under Languages.",
            file=sys.stderr,
        )
        return False

    # Step 3: Verify we have the expected skills
    expected_double_sparkle_skills = [
        "Photoshop",
        "Figma",
        "Notion",
        "Framer"
    ]
    
    expected_single_sparkle_skills = [
        "Webflow",
        "Rive",
        "CSS + Basic JS"
    ]
    
    # Check if all expected skills are present
    for skill_name in expected_double_sparkle_skills:
        found = any(skill_name in skill for skill in skills_with_double_sparkles)
        if not found:
            print(
                f"Error: Expected skill '{skill_name}' with ✨✨ not found.",
                file=sys.stderr,
            )
            return False
    
    for skill_name in expected_single_sparkle_skills:
        found = any(skill_name in skill for skill in skills_with_single_sparkle)
        if not found:
            print(
                f"Error: Expected skill '{skill_name}' with ✨ not found.",
                file=sys.stderr,
            )
            return False

    # Step 4: Verify Work History and Education sections have black placeholder images
    work_history_images_found = 0
    education_images_found = 0
    black_placeholder_url = "https://singlecolorimage.com/get/000000/"
    
    # Find Work History and Education sections in the right column
    right_column_id = None
    for column in columns:
        if column.get("type") == "column":
            width_ratio = column.get("column", {}).get("width_ratio", 0.5)
            # Right column has width_ratio around 0.75 or no width_ratio (which means equal split)
            if width_ratio > 0.6 or width_ratio == 0.5:
                right_column_id = column["id"]
                break
    
    if right_column_id:
        right_column_blocks = notion_utils.get_all_blocks_recursively(notion, right_column_id)
        
        # Find Work History section
        work_history_index = -1
        education_index = -1
        
        for i, block in enumerate(right_column_blocks):
            if block.get("type") == "heading_1":
                heading_text = notion_utils.get_block_plain_text(block)
                if "Work History" in heading_text:
                    work_history_index = i
                elif "Education" in heading_text:
                    education_index = i
        
        # Check Work History column lists for images
        if work_history_index != -1:
            for i in range(work_history_index + 1, min(education_index if education_index > work_history_index else len(right_column_blocks), len(right_column_blocks))):
                block = right_column_blocks[i]
                if block.get("type") == "column_list":
                    column_list_blocks = notion_utils.get_all_blocks_recursively(notion, block["id"])
                    for column in column_list_blocks:
                        if column.get("type") == "column":
                            # Check width_ratio - must be 50% (0.5) or absent (which defaults to 50%)
                            col_width = column.get("column", {}).get("width_ratio")
                            # First column should be image column (either no ratio=50%, or exactly 0.5)
                            if col_width is None or col_width == 0.5:
                                column_contents = notion_utils.get_all_blocks_recursively(notion, column["id"])
                                for content_block in column_contents:
                                    if content_block.get("type") == "embed":
                                        embed_url = content_block.get("embed", {}).get("url", "")
                                        if black_placeholder_url in embed_url:
                                            work_history_images_found += 1
                                    elif content_block.get("type") == "image":
                                        # Also check for image blocks with external URL
                                        image_url = content_block.get("image", {}).get("external", {}).get("url", "")
                                        if black_placeholder_url in image_url:
                                            work_history_images_found += 1
                                break  # Only check first column
        
        # Check Education column list for images
        if education_index != -1:
            for i in range(education_index + 1, len(right_column_blocks)):
                block = right_column_blocks[i]
                if block.get("type") == "heading_1":
                    break  # Stop at next section
                if block.get("type") == "column_list":
                    column_list_blocks = notion_utils.get_all_blocks_recursively(notion, block["id"])
                    for column in column_list_blocks:
                        if column.get("type") == "column":
                            # Check width_ratio - must be 50% (0.5) or absent (which defaults to 50%)
                            col_width = column.get("column", {}).get("width_ratio")
                            # First column should be image column (either no ratio=50%, or exactly 0.5)
                            if col_width is None or col_width == 0.5:
                                column_contents = notion_utils.get_all_blocks_recursively(notion, column["id"])
                                for content_block in column_contents:
                                    if content_block.get("type") == "embed":
                                        embed_url = content_block.get("embed", {}).get("url", "")
                                        if black_placeholder_url in embed_url:
                                            education_images_found += 1
                                    elif content_block.get("type") == "image":
                                        image_url = content_block.get("image", {}).get("external", {}).get("url", "")
                                        if black_placeholder_url in image_url:
                                            education_images_found += 1
                                break  # Only check first column
                    break  # Only check first column_list in Education
    
    # Verify images were found
    if work_history_images_found < 2:
        print(
            f"Warning: Expected at least 2 Work History images with black placeholder, found {work_history_images_found}.",
            file=sys.stderr,
        )
        return False
    
    if education_images_found < 1:
        print(
            f"Warning: Expected at least 1 Education image with black placeholder, found {education_images_found}.",
            file=sys.stderr,
        )
        return False
    
    print("Success: Skills display adjusted correctly.")
    print(f"- Found {len(skills_with_double_sparkles)} skills with ✨✨ (skill level >= 50%)")
    print(f"- Found {len(skills_with_single_sparkle)} skills with ✨ (skill level < 50%)")
    print("- Skills database removed from right column")
    print("- Skills section added to left column under Languages")
    print(f"- Found {work_history_images_found} Work History images with black placeholder")
    print(f"- Found {education_images_found} Education images with black placeholder")
    return True


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):
        sys.exit(0)
    else:
        sys.exit(1)


if __name__ == "__main__":
    main()