Layout Adjustment
L3
NotionOnline 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-4-1-opus | 0 /1 | - | - | 425.4s | 40.0 | 3,192,034 | 6,401 | 3,198,435 |
claude-4-sonnet | 0 /4 | 296.3s | 29.3 | 1,076,443 | 4,448 | 1,080,891 | ||
deepseek-chat | 0 /4 | 492.6s | 43.8 | 1,570,832 | 3,520 | 1,574,352 | ||
gemini-2-5-pro | 0 /4 | 92.0s | 9.3 | 108,538 | 4,010 | 112,548 | ||
gpt-5 | 0 /4 | 765.5s | 20.8 | 484,353 | 46,861 | 531,214 | ||
grok-4 | 0 /4 | - | - | - | - | - | ||
k2 | 0 /4 | 191.1s | 23.8 | 629,568 | 2,171 | 631,739 | ||
o3 | 0 /4 | 273.0s | 21.5 | 476,637 | 15,031 | 491,668 | ||
qwen-3-coder | 0 /4 | 330.4s | 38.0 | 1,546,518 | 5,488 | 1,552,006 |
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
Verify
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()