Film Inventory Management

L3
ModelContextProtocolPostgresDVD Rental

Manage film inventory through multiple operations including adding films, updating records, and cleaning old data.

Created by Lingxiao Du
2025-08-20
Data MigrationTransactional OperationsSchema Design

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-sonnet
4
/4
136.1s
23.8
178,406
4,607
183,013
DeepSeek
deepseek-chat
4
/4
196.8s
19.8
150,390
2,095
152,485
Gemini
gemini-2-5-pro
4
/4
89.7s
9.5
23,575
7,610
31,185
OpenAI
gpt-5
4
/4
393.2s
13.8
62,512
23,318
85,830
MoonshotAI
k2
4
/4
185.3s
22.5
175,619
2,633
178,252
Qwen
qwen-3-coder
4
/4
114.7s
24.5
274,075
2,477
276,552
Grok
grok-4
3
/4
61.7s
11.8
-
-
-
OpenAI
o3
3
/4
43.1s
6.0
22,277
2,988
25,265
Claude
claude-4-1-opus
1
/1
--
201.1s
13.0
57,692
3,526
61,218

Task State

Enum "mpaa_rating" { "G" "PG" "PG-13" "R" "NC-17" } Table "customer" { "customer_id" int4 [pk, not null, increment] "store_id" int2 [not null] "first_name" varchar(45) [not null] "last_name" varchar(45) [not null] "email" varchar(50) "address_id" int2 [not null] "activebool" bool [not null, default: true] "create_date" date [not null, default: `('now'::text)::date`] "last_update" timestamp [default: `now()`] "active" int4 Indexes { address_id [type: btree, name: "idx_fk_address_id"] store_id [type: btree, name: "idx_fk_store_id"] last_name [type: btree, name: "idx_last_name"] } } Table "actor" { "actor_id" int4 [pk, not null, increment] "first_name" varchar(45) [not null] "last_name" varchar(45) [not null] "last_update" timestamp [not null, default: `now()`] Indexes { last_name [type: btree, name: "idx_actor_last_name"] } } Table "category" { "category_id" int4 [pk, not null, increment] "name" varchar(25) [not null] "last_update" timestamp [not null, default: `now()`] } Table "film" { "film_id" int4 [pk, not null, increment] "title" varchar(255) [not null] "description" text "release_year" int4 "language_id" int2 [not null] "rental_duration" int2 [not null, default: 3] "rental_rate" numeric(4,2) [not null, default: 4.99] "length" int2 "replacement_cost" numeric(5,2) [not null, default: 19.99] "rating" mpaa_rating [default: 'G'] "last_update" timestamp [not null, default: `now()`] "special_features" "text[]" "fulltext" tsvector [not null] Indexes { fulltext [type: gist, name: "film_fulltext_idx"] language_id [type: btree, name: "idx_fk_language_id"] title [type: btree, name: "idx_title"] } } Table "film_actor" { "actor_id" int2 [not null] "film_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { (actor_id, film_id) [type: btree, name: "film_actor_pkey"] film_id [type: btree, name: "idx_fk_film_id"] } } Table "film_category" { "film_id" int2 [not null] "category_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { (film_id, category_id) [type: btree, name: "film_category_pkey"] } } Table "address" { "address_id" int4 [pk, not null, increment] "address" varchar(50) [not null] "address2" varchar(50) "district" varchar(20) [not null] "city_id" int2 [not null] "postal_code" varchar(10) "phone" varchar(20) [not null] "last_update" timestamp [not null, default: `now()`] Indexes { city_id [type: btree, name: "idx_fk_city_id"] } } Table "city" { "city_id" int4 [pk, not null, increment] "city" varchar(50) [not null] "country_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { country_id [type: btree, name: "idx_fk_country_id"] } } Table "country" { "country_id" int4 [pk, not null, increment] "country" varchar(50) [not null] "last_update" timestamp [not null, default: `now()`] } Table "inventory" { "inventory_id" int4 [pk, not null, increment] "film_id" int2 [not null] "store_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { (store_id, film_id) [type: btree, name: "idx_store_id_film_id"] } } Table "language" { "language_id" int4 [pk, not null, increment] "name" bpchar(20) [not null] "last_update" timestamp [not null, default: `now()`] } Table "payment" { "payment_id" int4 [pk, not null, increment] "customer_id" int2 [not null] "staff_id" int2 [not null] "rental_id" int4 [not null] "amount" numeric(5,2) [not null] "payment_date" timestamp [not null] Indexes { rental_id [type: btree, name: "idx_fk_rental_id"] staff_id [type: btree, name: "idx_fk_staff_id"] } } Table "rental" { "rental_id" int4 [pk, not null, increment] "rental_date" timestamp [not null] "inventory_id" int4 [not null] "customer_id" int2 [not null] "return_date" timestamp "staff_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { (rental_date, inventory_id, customer_id) [type: btree, name: "idx_unq_rental_rental_date_inventory_id_customer_id"] inventory_id [type: btree, name: "idx_fk_inventory_id"] } } Table "staff" { "staff_id" int4 [pk, not null, increment] "first_name" varchar(45) [not null] "last_name" varchar(45) [not null] "address_id" int2 [not null] "email" varchar(50) "store_id" int2 [not null] "active" bool [not null, default: true] "username" varchar(16) [not null] "password" varchar(40) "last_update" timestamp [not null, default: `now()`] "picture" bytea } Table "store" { "store_id" int4 [pk, not null, increment] "manager_staff_id" int2 [unique, not null] "address_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] } Ref "fk_address_city":"city"."city_id" < "address"."city_id" Ref "fk_city":"country"."country_id" < "city"."country_id" Ref "customer_address_id_fkey":"address"."address_id" < "customer"."address_id" [update: cascade, delete: restrict] Ref "film_language_id_fkey":"language"."language_id" < "film"."language_id" [update: cascade, delete: restrict] Ref "film_actor_actor_id_fkey":"actor"."actor_id" < "film_actor"."actor_id" [update: cascade, delete: restrict] Ref "film_actor_film_id_fkey":"film"."film_id" < "film_actor"."film_id" [update: cascade, delete: restrict] Ref "film_category_category_id_fkey":"category"."category_id" < "film_category"."category_id" [update: cascade, delete: restrict] Ref "film_category_film_id_fkey":"film"."film_id" < "film_category"."film_id" [update: cascade, delete: restrict] Ref "inventory_film_id_fkey":"film"."film_id" < "inventory"."film_id" [update: cascade, delete: restrict] Ref "payment_customer_id_fkey":"customer"."customer_id" < "payment"."customer_id" [update: cascade, delete: restrict] Ref "payment_rental_id_fkey":"rental"."rental_id" < "payment"."rental_id" [update: cascade, delete: set null] Ref "payment_staff_id_fkey":"staff"."staff_id" < "payment"."staff_id" [update: cascade, delete: restrict] Ref "rental_customer_id_fkey":"customer"."customer_id" < "rental"."customer_id" [update: cascade, delete: restrict] Ref "rental_inventory_id_fkey":"inventory"."inventory_id" < "rental"."inventory_id" [update: cascade, delete: restrict] Ref "rental_staff_id_key":"staff"."staff_id" < "rental"."staff_id" Ref "staff_address_id_fkey":"address"."address_id" < "staff"."address_id" [update: cascade, delete: restrict] Ref "store_address_id_fkey":"address"."address_id" < "store"."address_id" [update: cascade, delete: restrict] Ref "store_manager_staff_id_fkey":"staff"."staff_id" < "store"."manager_staff_id" [update: cascade, delete: restrict]

Instruction



Verify

*.py
Python
"""
Verification script for PostgreSQL Task 4: Film Inventory Management
"""

import os
import sys
import psycopg2
from decimal import Decimal

def rows_match(actual_row, expected_row):
    """Compare two rows with appropriate tolerance for decimals and floats."""
    if len(actual_row) != len(expected_row):
        return False
    
    for actual, expected in zip(actual_row, expected_row):
        if isinstance(actual, (Decimal, float)) and isinstance(expected, (Decimal, float)):
            # Use higher tolerance for floating point comparisons
            if abs(float(actual) - float(expected)) > 0.01:
                return False
        elif actual != expected:
            return False
    
    return True

def get_connection_params() -> dict:
    """Get database connection parameters."""
    return {
        "host": os.getenv("POSTGRES_HOST", "localhost"),
        "port": int(os.getenv("POSTGRES_PORT", 5432)),
        "database": os.getenv("POSTGRES_DATABASE"),
        "user": os.getenv("POSTGRES_USERNAME"),
        "password": os.getenv("POSTGRES_PASSWORD")
    }

def check_new_films(conn) -> bool:
    """Check if the two new films were added correctly."""
    with conn.cursor() as cur:
        cur.execute("""
            SELECT title, description, release_year, language_id, 
                   rental_duration, rental_rate, length, replacement_cost, 
                   rating
            FROM film 
            WHERE title IN ('Data Science Adventures', 'Cloud Computing Chronicles')
            ORDER BY title
        """)
        actual_films = cur.fetchall()
        
        expected_films = [
            ('Cloud Computing Chronicles', 'Exploring the world of distributed systems', 2024, 1, 7, Decimal('4.99'), 135, Decimal('18.99'), 'PG'),
            ('Data Science Adventures', 'A thrilling journey through machine learning algorithms', 2024, 1, 5, Decimal('4.389'), 120, Decimal('15.99'), 'PG-13')
        ]
        
        if len(actual_films) != len(expected_films):
            print(f"❌ Expected {len(expected_films)} new films, found {len(actual_films)}")
            return False
            
        mismatches = 0
        for i, (actual, expected) in enumerate(zip(actual_films, expected_films)):
            if not rows_match(actual, expected):
                print(f"❌ Film {i+1} mismatch: expected {expected}, got {actual}")
                mismatches += 1
                
        if mismatches > 0:
            print(f"❌ Total film mismatches: {mismatches}")
            return False
            
        print("✅ Both new films added correctly")
        return True

def check_inventory_records(conn) -> bool:
    """Check if inventory records were added for new films."""
    with conn.cursor() as cur:
        cur.execute("""
            SELECT f.title, i.store_id, COUNT(*) as count
            FROM film f
            JOIN inventory i ON f.film_id = i.film_id
            WHERE f.title IN ('Data Science Adventures', 'Cloud Computing Chronicles')
            GROUP BY f.title, i.store_id
            ORDER BY f.title, i.store_id
        """)
        actual_inventory = cur.fetchall()
        
        expected_inventory = [
            ('Cloud Computing Chronicles', 1, 3),
            ('Cloud Computing Chronicles', 2, 2), 
            ('Data Science Adventures', 1, 3),
            ('Data Science Adventures', 2, 2)
        ]
        
        if len(actual_inventory) != len(expected_inventory):
            print(f"❌ Expected {len(expected_inventory)} inventory groups, found {len(actual_inventory)}")
            return False
            
        mismatches = 0
        for i, (actual, expected) in enumerate(zip(actual_inventory, expected_inventory)):
            if not rows_match(actual, expected):
                print(f"❌ Inventory group {i+1} mismatch: expected {expected}, got {actual}")
                mismatches += 1
                
        if mismatches > 0:
            print(f"❌ Total inventory mismatches: {mismatches}")
            return False
                
        print("✅ Inventory records added correctly")
        return True

def check_available_films_table(conn) -> bool:
    """Check if available_films table was created and populated correctly."""
    with conn.cursor() as cur:
        # Get actual results from the created table
        cur.execute("""
            SELECT film_id, title, rental_rate, length
            FROM available_films
            ORDER BY rental_rate DESC, length DESC, title ASC
        """)
        actual_results = cur.fetchall()
        
        # Execute ground truth query
        cur.execute("""
            SELECT DISTINCT f.film_id, f.title, f.rental_rate, f.length
            FROM film f
            JOIN inventory i ON f.film_id = i.film_id
            WHERE f.rental_rate >= 3.00 AND f.rental_rate <= 5.00
            AND f.length > 100
            AND i.store_id = 1
            ORDER BY f.rental_rate DESC, f.length DESC, f.title ASC
        """)
        expected_results = cur.fetchall()
        
        if len(actual_results) != len(expected_results):
            print(f"❌ available_films table has {len(actual_results)} records, expected {len(expected_results)}")
            return False
            
        mismatches = 0
        for i, (actual, expected) in enumerate(zip(actual_results, expected_results)):
            if not rows_match(actual, expected):
                if mismatches < 5:  # Only show first 5 mismatches
                    print(f"❌ available_films row {i+1} mismatch: expected {expected}, got {actual}")
                mismatches += 1
                
        if mismatches > 0:
            print(f"❌ Total available_films mismatches: {mismatches}")
            return False
            
        print(f"✅ available_films table created and populated correctly ({len(actual_results)} records)")
        return True

def check_inventory_cleanup(conn) -> bool:
    """Check if inventory cleanup was performed correctly."""
    with conn.cursor() as cur:
        # Check that no inventory exists for films with replacement_cost > 25 AND rental_rate < 1
        # that also don't have rental records (safe to delete)
        cur.execute("""
            SELECT COUNT(*)
            FROM inventory i
            JOIN film f ON i.film_id = f.film_id
            WHERE f.replacement_cost > 25.00 AND f.rental_rate < 1.00
            AND NOT EXISTS (SELECT 1 FROM rental r WHERE r.inventory_id = i.inventory_id)
        """)
        
        remaining_count = cur.fetchone()[0]
        
        if remaining_count > 0:
            print(f"❌ Found {remaining_count} inventory records that should have been deleted (no rental history)")
            return False
            
        print("✅ Inventory cleanup completed correctly")
        return True

def check_summary_table(conn) -> bool:
    """Check if film_inventory_summary table was created and populated correctly."""
    with conn.cursor() as cur:
            
        # Get actual results from the created table
        cur.execute("""
            SELECT title, rental_rate, total_inventory, store1_count, store2_count
            FROM film_inventory_summary
        """)
        actual_results = cur.fetchall()
        
        # Execute ground truth query
        cur.execute("""
            SELECT f.title, f.rental_rate,
                   COUNT(i.inventory_id) as total_inventory,
                   COUNT(CASE WHEN i.store_id = 1 THEN 1 END) as store1_count,
                   COUNT(CASE WHEN i.store_id = 2 THEN 1 END) as store2_count
            FROM film f
            JOIN inventory i ON f.film_id = i.film_id
            GROUP BY f.film_id, f.title, f.rental_rate
            ORDER BY total_inventory DESC, f.title ASC
        """)
        expected_results = cur.fetchall()
        
        if len(actual_results) != len(expected_results):
            print(f"❌ film_inventory_summary table has {len(actual_results)} records, expected {len(expected_results)}")
            return False
            
        mismatches = 0
        for i, (actual, expected) in enumerate(zip(actual_results, expected_results)):
            if not rows_match(actual, expected):
                if mismatches < 5:  # Only show first 5 mismatches
                    print(f"❌ Summary row {i+1} mismatch: expected {expected}, got {actual}")
                mismatches += 1
                
        if mismatches > 0:
            print(f"❌ Total summary table mismatches: {mismatches}")
            return False
                
        print(f"✅ film_inventory_summary table created and populated correctly ({len(actual_results)} records)")
        return True

def main():
    """Main verification function."""
    print("=" * 70)
    print("PostgreSQL Task 4 Verification: Film Inventory Management")
    print("=" * 70)
    
    # Get connection parameters
    conn_params = get_connection_params()
    
    if not conn_params["database"]:
        print("❌ No database specified")
        sys.exit(1)
    
    try:
        # Connect to database
        conn = psycopg2.connect(**conn_params)
        
        # Verify all operations with short-circuit evaluation
        success = (
            check_new_films(conn) and 
            check_inventory_records(conn) and
            check_available_films_table(conn) and 
            check_inventory_cleanup(conn) and
            check_summary_table(conn)
        )
        
        conn.close()
        
        if success:
            print(f"\n🎉 Task verification: PASS")
            sys.exit(0)
        else:
            print(f"\n❌ Task verification: FAIL")
            sys.exit(1)
            
    except psycopg2.Error as e:
        print(f"❌ Database error: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"❌ Verification error: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()