Weekend Adventure Planner

L3
ModelContextProtocolNotionToronto Guide

Create a comprehensive weekend adventure planner that analyzes Toronto Guide databases and generates a structured itinerary page.

Created by Xiangyan Liu
2025-08-14
Conditional FilteringData AggregationReport GenerationVisual FormattingStatus Tracking

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
OpenAI
gpt-5-high
4
/4
427.6s
7.0
174,737
27,242
201,979
OpenAI
gpt-5-medium
4
/4
344.5s
6.5
144,493
16,221
160,713
OpenAI
gpt-5-mini-high
4
/4
175.2s
9.3
126,620
17,271
143,891
Qwen
qwen-3-max
4
/4
74.6s
13.3
230,021
1,759
231,780
OpenAI
gpt-5-low
3
/4
279.7s
6.5
178,200
14,398
192,598
Claude
claude-sonnet-4
2
/4
143.0s
13.5
244,474
3,690
248,164
Claude
claude-sonnet-4-high
2
/4
184.8s
13.3
1,635,755
3,922
1,639,677
Claude
claude-sonnet-4-low
2
/4
267.3s
13.5
1,449,181
3,771
1,452,952
Gemini
gemini-2-5-pro
2
/4
42.3s
5.8
87,897
2,302
90,199
Claude
claude-opus-4-1
1
/1
--
208.8s
10.0
151,321
3,160
154,481
Gemini
gemini-2-5-flash
1
/4
29.8s
3.5
64,802
3,150
67,952
Z.ai
glm-4-5
1
/4
431.8s
38.8
1,115,251
9,144
1,124,395
OpenAI
gpt-4-1
1
/4
48.1s
6.5
104,198
1,416
105,614
OpenAI
gpt-oss-120b
1
/4
38.6s
6.5
155,686
2,334
158,020
MoonshotAI
kimi-k2-0711
1
/4
76.3s
8.0
89,325
1,788
91,113
OpenAI
o3
1
/4
86.1s
11.5
105,766
4,796
110,561
OpenAI
o4-mini
1
/4
209.1s
10.0
90,903
11,486
102,389
DeepSeek
deepseek-chat
0
/4
162.4s
10.0
156,851
2,038
158,889
OpenAI
gpt-4-1-mini
0
/4
46.8s
9.0
109,916
1,473
111,390
OpenAI
gpt-4-1-nano
0
/4
22.3s
7.5
46,617
820
47,437
OpenAI
gpt-5-mini-low
0
/4
48.5s
7.5
125,316
2,596
127,912
OpenAI
gpt-5-mini-medium
0
/4
61.4s
7.8
96,135
4,796
100,930
OpenAI
gpt-5-nano-high
0
/4
360.7s
11.5
231,866
71,743
303,609
OpenAI
gpt-5-nano-low
0
/4
95.3s
6.5
79,642
16,194
95,835
OpenAI
gpt-5-nano-medium
0
/4
213.2s
8.8
180,229
46,101
226,330
Grok
grok-4
0
/4
510.0s
10.8
249,467
8,971
258,438
Grok
grok-code-fast-1
0
/4
88.2s
20.3
459,446
8,049
468,920
MoonshotAI
kimi-k2-0905
0
/4
328.8s
17.5
324,025
4,536
328,561
Qwen
qwen-3-coder-plus
0
/4
69.7s
15.8
293,836
1,971
295,807

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

Create a comprehensive weekend adventure planner that analyzes the Toronto Guide databases and generates a structured itinerary page. I need you to create a new page called 'Perfect Weekend Adventure' as a child of the main Toronto Guide page.

Task Requirements:

  1. Create a new page titled 'Perfect Weekend Adventure' as a child page of the main Toronto Guide page
  2. Query the Activities database to identify all activities that have the "Beaches" tag
  3. Query the Food database to find all restaurants with "Turkish" or "Hakka" tags
  4. Query the Cafes database to retrieve all cafes entries
  5. Structure the page with the following specific format:
    • Add a heading_1 block with text "🎒 Perfect Weekend Adventure"
    • Add a heading_2 block with text "🏖️ Beach Activities"
    • Under Beach Activities, create a bulleted list with all activities that have the "Beaches" tag, showing: Name - Google Maps Link (if available)
    • Add a heading_2 block with text "🍽️ Cultural Dining Experience"
    • Under Cultural Dining, create a numbered list of all restaurants with "Turkish" or "Hakka" tags, formatted as: Restaurant Name (Tag: [actual tag name])
    • Add a heading_2 block with text "☕ Coffee Break Spots"
    • Under Coffee Break Spots, create a toggle block titled "Top Cafes to Visit" containing all cafe entries as to-do items (unchecked), each showing just the cafe name
    • Add a heading_2 block with text "📊 Weekend Summary"
    • Under Weekend Summary, add a paragraph with the exact text: "This weekend includes [X] beach activities, [Y] cultural dining options, and [Z] coffee spots to explore!" where [X], [Y], and [Z] are the actual counts
  6. After the summary paragraph, add a divider block
  7. Finally, add a callout block with the 💡 emoji containing the text: "Pro tip: Check the Seasons database for the best time to enjoy outdoor activities!"
  8. Ensure all headings use the exact emoji and text format specified above
  9. The lists must be in the exact format specified (bulleted for beaches, numbered for restaurants, to-do for cafes)


Verify

*.py
Python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

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 Perfect Weekend Adventure page has been created correctly.
    """
    # Find the main Toronto Guide page
    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, "Toronto Guide")
    if not page_id:
        print("Error: Main 'Toronto Guide' page not found.", file=sys.stderr)
        return False
    
    # Find the Perfect Weekend Adventure child page
    adventure_page_id = None
    try:
        response = notion.search(
            query="Perfect Weekend Adventure",
            filter={"property": "object", "value": "page"}
        )
        
        for result in response.get("results", []):
            parent = result.get("parent", {})
            if parent.get("type") == "page_id" and parent.get("page_id") == page_id:
                adventure_page_id = result["id"]
                break
        
        if not adventure_page_id:
            for result in response.get("results", []):
                title_list = result.get("properties", {}).get("title", {}).get("title", [])
                for title_obj in title_list:
                    if "Perfect Weekend Adventure" in title_obj.get("plain_text", ""):
                        adventure_page_id = result["id"]
                        break
                if adventure_page_id:
                    break
    
    except Exception as e:
        print(f"Error searching for Perfect Weekend Adventure page: {e}", file=sys.stderr)
        return False
    
    if not adventure_page_id:
        print("Error: 'Perfect Weekend Adventure' page not found as child of main page.", file=sys.stderr)
        return False
    
    # Get all blocks from the adventure page
    all_blocks = notion_utils.get_all_blocks_recursively(notion, adventure_page_id)
    
    # Get databases from the main Toronto Guide page
    activities_db_id = None
    food_db_id = None
    cafes_db_id = None
    
    main_blocks = notion_utils.get_all_blocks_recursively(notion, page_id)
    for block in main_blocks:
        if block.get("type") == "child_database":
            title = block.get("child_database", {}).get("title", "")
            if "Activities" in title:
                activities_db_id = block.get("id")
            elif "Food" in title:
                food_db_id = block.get("id")
            elif "Cafes" in title or "Caf�" in title:
                cafes_db_id = block.get("id")
    
    # Query databases to get expected data
    beach_activities = []
    cultural_restaurants = []
    cafes_list = []
    
    if activities_db_id:
        try:
            db_response = notion.databases.query(database_id=activities_db_id)
            for page in db_response.get("results", []):
                properties = page.get("properties", {})
                tags_prop = properties.get("Tags", {})
                if tags_prop.get("type") == "multi_select":
                    tags = [tag.get("name") for tag in tags_prop.get("multi_select", [])]
                    if "Beaches" in tags:
                        name_prop = properties.get("Name", {})
                        if name_prop.get("type") == "title" and name_prop.get("title"):
                            name = name_prop["title"][0]["plain_text"]
                            url_prop = properties.get("Google Maps Link", {})
                            url = url_prop.get("url", "") if url_prop.get("type") == "url" else ""
                            beach_activities.append({"name": name, "url": url})
        except Exception as e:
            print(f"Error querying Activities database: {e}", file=sys.stderr)
            return False
    
    if food_db_id:
        try:
            db_response = notion.databases.query(database_id=food_db_id)
            for page in db_response.get("results", []):
                properties = page.get("properties", {})
                tags_prop = properties.get("Tags", {})
                if tags_prop.get("type") == "multi_select":
                    tags = [tag.get("name") for tag in tags_prop.get("multi_select", [])]
                    for tag in tags:
                        if tag in ["Turkish", "Hakka"]:
                            name_prop = properties.get("Name", {})
                            if name_prop.get("type") == "title" and name_prop.get("title"):
                                name = name_prop["title"][0]["plain_text"]
                                cultural_restaurants.append({"name": name, "tag": tag})
                                break
        except Exception as e:
            print(f"Error querying Food database: {e}", file=sys.stderr)
            return False
    
    if cafes_db_id:
        try:
            db_response = notion.databases.query(database_id=cafes_db_id)
            for page in db_response.get("results", []):
                properties = page.get("properties", {})
                name_prop = properties.get("Name", {})
                if name_prop.get("type") == "title" and name_prop.get("title"):
                    name = name_prop["title"][0]["plain_text"]
                    cafes_list.append(name)
        except Exception as e:
            print(f"Error querying Cafes database: {e}", file=sys.stderr)
            return False
    
    # Required headings and their types
    required_headings = [
        ("🎒 Perfect Weekend Adventure", "heading_1"),
        ("🏖️ Beach Activities", "heading_2"),
        ("🍽️ Cultural Dining Experience", "heading_2"),
        ("☕ Coffee Break Spots", "heading_2"),
        ("📊 Weekend Summary", "heading_2")
    ]
    
    # Track verification results
    found_headings = set()
    found_beach_list = False
    found_restaurant_list = False
    found_toggle_with_cafes = False
    found_summary = False
    found_divider = False
    found_callout = False
    
    # Variables to track counts
    beach_count = 0
    restaurant_count = 0
    cafe_count = 0
    
    current_section = None
    is_in_toggle = False
    
    for block in all_blocks:
        block_type = block.get("type")
        block_text = notion_utils.get_block_plain_text(block)
        
        # Check headings
        for heading_text, expected_type in required_headings:
            if heading_text in block_text and block_type == expected_type:
                found_headings.add(heading_text)
                current_section = heading_text
        
        # Check Beach Activities section
        if current_section == "🏖️ Beach Activities" and block_type == "bulleted_list_item":
            found_beach_list = True
            beach_count += 1
            # Verify format includes name and potentially URL
            for activity in beach_activities:
                if activity["name"] in block_text:
                    if activity["url"] and activity["url"] not in block_text:
                        print(f"Warning: Beach activity '{activity['name']}' missing URL", file=sys.stderr)
        
        # Check Cultural Dining section
        elif current_section == "🍽️ Cultural Dining Experience" and block_type == "numbered_list_item":
            found_restaurant_list = True
            restaurant_count += 1
            # Check format: Restaurant Name (Tag: [tag])
            for restaurant in cultural_restaurants:
                if restaurant["name"] in block_text and f"Tag: {restaurant['tag']}" in block_text:
                    pass  # Format is correct
        
        # Check Coffee Break Spots section
        elif current_section == "☕ Coffee Break Spots":
            if block_type == "toggle" and "Top Cafes to Visit" in block_text:
                is_in_toggle = True
                found_toggle_with_cafes = True
            elif is_in_toggle and block_type == "to_do":
                cafe_count += 1
                # Verify unchecked status
                to_do_data = block.get("to_do", {})
                if to_do_data.get("checked", False):
                    print(f"Error: Cafe to-do item should be unchecked: {block_text}", file=sys.stderr)
                    return False
            elif block_type in ["heading_1", "heading_2", "heading_3"]:
                is_in_toggle = False
        
        # Check Weekend Summary section
        elif current_section == "📊 Weekend Summary" and block_type == "paragraph":
            expected_text = f"This weekend includes {len(beach_activities)} beach activities, {len(cultural_restaurants)} cultural dining options, and {len(cafes_list)} coffee spots to explore!"
            if expected_text in block_text:
                found_summary = True
        
        # Check for divider after summary
        if block_type == "divider":
            found_divider = True
        
        # Check for callout with pro tip
        if block_type == "callout":
            callout_data = block.get("callout", {})
            icon = callout_data.get("icon", {})
            if icon.get("type") == "emoji" and icon.get("emoji") == "💡":
                if "Pro tip: Check the Seasons database for the best time to enjoy outdoor activities!" in block_text:
                    found_callout = True
    
    # Verify all required elements
    all_passed = True
    
    # Check all headings are present
    for heading_text, _ in required_headings:
        if heading_text not in found_headings:
            print(f"Error: Missing required heading: {heading_text}", file=sys.stderr)
            all_passed = False
    
    # Check beach activities list
    if not found_beach_list:
        print("Error: Beach activities bulleted list not found", file=sys.stderr)
        all_passed = False
    elif beach_count != len(beach_activities):
        print(f"Error: Expected {len(beach_activities)} beach activities, found {beach_count}", file=sys.stderr)
        all_passed = False
    
    # Check restaurant list
    if not found_restaurant_list:
        print("Error: Cultural dining numbered list not found", file=sys.stderr)
        all_passed = False
    elif restaurant_count != len(cultural_restaurants):
        print(f"Error: Expected {len(cultural_restaurants)} cultural restaurants, found {restaurant_count}", file=sys.stderr)
        all_passed = False
    
    # Check cafes toggle
    if not found_toggle_with_cafes:
        print("Error: Toggle block 'Top Cafes to Visit' not found", file=sys.stderr)
        all_passed = False
    elif cafe_count != len(cafes_list):
        print(f"Error: Expected {len(cafes_list)} cafes, found {cafe_count}", file=sys.stderr)
        all_passed = False
    
    # Check summary
    if not found_summary:
        print("Error: Weekend summary with correct counts not found", file=sys.stderr)
        all_passed = False
    
    # Check divider
    if not found_divider:
        print("Error: Divider block not found after summary", file=sys.stderr)
        all_passed = False
    
    # Check callout
    if not found_callout:
        print("Error: Callout with pro tip not found", file=sys.stderr)
        all_passed = False
    
    if all_passed:
        print(f"Success: Perfect Weekend Adventure page created with all required elements.")
        print(f"- {len(beach_activities)} beach activities")
        print(f"- {len(cultural_restaurants)} cultural dining options")
        print(f"- {len(cafes_list)} coffee spots")
        return True
    else:
        return False


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()