Courses Internships Relation

L3
ModelContextProtocolNotionComputer Science Student Dashboard

Connect the Courses and Internship search databases with bidirectional relations and populate with sample data.

Created by Zijian Wu
2025-07-27
Database ManipulationCross Reference LinkingTemplate 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-4-1-opus
0
/1
--
363.5s
21.0
420,966
4,170
425,136
Claude
claude-4-sonnet
0
/4
215.0s
24.3
630,677
5,208
635,885
DeepSeek
deepseek-chat
0
/4
267.7s
22.0
398,360
2,579
400,938
Gemini
gemini-2-5-pro
0
/4
60.7s
2.0
8,971
3,183
12,154
OpenAI
gpt-5
0
/4
912.1s
18.3
228,376
52,685
281,061
Grok
grok-4
0
/4
32.9s
-
-
-
-
MoonshotAI
k2
0
/4
104.2s
17.5
215,842
2,218
218,060
OpenAI
o3
0
/4
489.8s
33.5
435,148
35,544
470,692
Qwen
qwen-3-coder
0
/4
175.5s
30.0
462,319
3,136
465,455

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

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

# ---------------------------------------------------------------------------
# Constants -----------------------------------------------------------------
# ---------------------------------------------------------------------------
MAIN_PAGE_TITLE = "Computer Science Student Dashboard"
COURSES_DB_TITLE = "Courses"
INTERNSHIP_DB_TITLE = "Internship search"

COURSE_CODES = {"CS301", "CS302", "CS303"}
COURSE_RELATION_NAME = "Related Internships"
INTERNSHIP_RELATION_NAME = "Relevant Courses"

INTERNSHIP_COMPANIES = {"OpenAI", "Google"}

# ---------------------------------------------------------------------------
# Helper functions -----------------------------------------------------------
# ---------------------------------------------------------------------------


def _locate_main_page(notion: Client, main_id: str | None) -> str | None:
    """Return the page_id of the dashboard page or None if not found."""
    page_id = None
    if main_id:
        found_id, obj_type = notion_utils.find_page_or_database_by_id(notion, main_id)
        if found_id and obj_type == "page":
            page_id = found_id
    if not page_id:
        page_id = notion_utils.find_page(notion, MAIN_PAGE_TITLE)
    return page_id


def _locate_database(notion: Client, parent_page_id: str, db_title: str) -> str | None:
    """Recursively search for a child database by title and return its id."""
    return notion_utils.find_database_in_block(notion, parent_page_id, db_title)


# ---------------------------------------------------------------------------
# Verification logic ---------------------------------------------------------
# ---------------------------------------------------------------------------


def verify(notion: Client, main_id: str | None = None) -> bool:
    """Verify completion of the Courses ↔ Internship relation task."""
    # ------------------------------------------------------------------
    # Locate main page and databases -----------------------------------
    # ------------------------------------------------------------------
    page_id = _locate_main_page(notion, main_id)
    if not page_id:
        print(f"Error: Page '{MAIN_PAGE_TITLE}' not found.", file=sys.stderr)
        return False

    courses_db_id = _locate_database(notion, page_id, COURSES_DB_TITLE)
    internships_db_id = _locate_database(notion, page_id, INTERNSHIP_DB_TITLE)

    if not courses_db_id:
        print(f"Error: Database '{COURSES_DB_TITLE}' not found.", file=sys.stderr)
        return False
    if not internships_db_id:
        print(f"Error: Database '{INTERNSHIP_DB_TITLE}' not found.", file=sys.stderr)
        return False

    # ------------------------------------------------------------------
    # Validate relation properties -------------------------------------
    # ------------------------------------------------------------------
    courses_db_obj = notion.databases.retrieve(database_id=courses_db_id)
    internships_db_obj = notion.databases.retrieve(database_id=internships_db_id)

    courses_props = courses_db_obj.get("properties", {})
    internships_props = internships_db_obj.get("properties", {})

    # Courses → Internships relation
    if COURSE_RELATION_NAME not in courses_props:
        print(
            f"Error: Property '{COURSE_RELATION_NAME}' missing in Courses database.",
            file=sys.stderr,
        )
        return False
    course_rel_prop = courses_props[COURSE_RELATION_NAME]
    if (
        course_rel_prop.get("type") != "relation"
        or course_rel_prop["relation"].get("database_id") != internships_db_id
    ):
        print(
            "Error: Courses relation property is not configured correctly.",
            file=sys.stderr,
        )
        return False

    # Internships → Courses relation
    if INTERNSHIP_RELATION_NAME not in internships_props:
        print(
            f"Error: Property '{INTERNSHIP_RELATION_NAME}' missing in Internship search database.",
            file=sys.stderr,
        )
        return False
    intern_rel_prop = internships_props[INTERNSHIP_RELATION_NAME]
    if (
        intern_rel_prop.get("type") != "relation"
        or intern_rel_prop["relation"].get("database_id") != courses_db_id
    ):
        print(
            "Error: Internship relation property is not configured correctly.",
            file=sys.stderr,
        )
        return False

    # ------------------------------------------------------------------
    # Validate course pages --------------------------------------------
    # ------------------------------------------------------------------
    course_pages = notion.databases.query(database_id=courses_db_id).get("results", [])

    valid_course_count = 0
    course_page_id_set = set()
    internship_ids_seen: set[str] = set()

    for page in course_pages:
        props = page.get("properties", {})
        code_rts = props.get("Code", {}).get("rich_text", [])
        code_val = "".join(rt.get("plain_text", "") for rt in code_rts).strip()
        if code_val not in COURSE_CODES:
            continue  # not one of the new course entries we care about

        # Check required scalar props
        title_rts = props.get("Name", {}).get("title", [])
        name_ok = bool("".join(rt.get("plain_text", "") for rt in title_rts).strip())
        credits_ok = props.get("Credit", {}).get("number") is not None
        status_name = props.get("Status", {}).get("status", {}).get("name", "")
        status_allowed = {"planned", "in progress", "completed"}
        status_ok = status_name.lower() in status_allowed

        # Relation must point to at least one internship
        relations = props.get(COURSE_RELATION_NAME, {}).get("relation", [])
        if not (name_ok and credits_ok and status_ok and relations):
            print(
                f"Error: Course '{code_val}' is missing required property values or relations, or wrong values.",
                file=sys.stderr,
            )
            return False

        # Collect IDs for further mutual check
        course_page_id_set.add(page["id"])
        internship_ids_seen.update(rel["id"] for rel in relations)
        valid_course_count += 1

    if valid_course_count != 3:
        print(
            f"Error: Expected exactly 3 new course pages with codes {COURSE_CODES}, found {valid_course_count}.",
            file=sys.stderr,
        )
        return False

    # ------------------------------------------------------------------
    # Validate internship pages ----------------------------------------
    # ------------------------------------------------------------------
    internship_pages = notion.databases.query(database_id=internships_db_id).get(
        "results", []
    )

    valid_intern_count = 0
    internship_page_ids = set()
    course_ids_seen_from_intern: set[str] = set()

    for page in internship_pages:
        props = page.get("properties", {})
        company_rts = props.get("Company", {}).get("rich_text", [])
        company = "".join(rt.get("plain_text", "") for rt in company_rts).strip()
        if company not in INTERNSHIP_COMPANIES:
            continue  # not one of the two new internships

        role_rts = props.get("Role", {}).get("title", [])
        role_ok = bool("".join(rt.get("plain_text", "") for rt in role_rts).strip())
        status_name = props.get("Status", {}).get("status", {}).get("name", "")
        status_ok = status_name.lower() == "interested"
        relations = props.get(INTERNSHIP_RELATION_NAME, {}).get("relation", [])

        if not (role_ok and status_ok and relations):
            print(
                f"Error: Internship at '{company}' is missing required property values or relations, or wrong values.",
                file=sys.stderr,
            )
            return False

        internship_page_ids.add(page["id"])
        course_ids_seen_from_intern.update(rel["id"] for rel in relations)
        valid_intern_count += 1

    if valid_intern_count != 2:
        print(
            f"Error: Expected exactly 2 new internship pages for companies {INTERNSHIP_COMPANIES}, found {valid_intern_count}.",
            file=sys.stderr,
        )
        return False

    # ------------------------------------------------------------------
    # Mutual relation consistency --------------------------------------
    # ------------------------------------------------------------------
    # Each relation from courses should point to one of the two internships identified
    if not internship_ids_seen.issubset(internship_page_ids):
        print(
            "Error: Some course relations point to pages outside the expected internships.",
            file=sys.stderr,
        )
        return False

    # Each relation from internships should point back to the three course pages identified
    if not course_ids_seen_from_intern.issubset(course_page_id_set):
        print(
            "Error: Some internship relations point to pages outside the expected courses.",
            file=sys.stderr,
        )
        return False

    print(
        "Success: Verified bidirectional relations, course and internship entries as required."
    )
    return True


# ---------------------------------------------------------------------------
# CLI entry-point -----------------------------------------------------------
# ---------------------------------------------------------------------------


def main() -> None:
    notion = notion_utils.get_notion_client()
    main_id = sys.argv[1] if len(sys.argv) > 1 else None
    sys.exit(0 if verify(notion, main_id) else 1)


if __name__ == "__main__":
    main()