59 lines
30 KiB
JSON
59 lines
30 KiB
JSON
[
|
|
{
|
|
"id": "20e16006-8021-4434-a6c9-3b370efde0f8",
|
|
"sort_order": 0,
|
|
"rubric_text": "In file `ap_ledger.xlsx`, Invoice Register sheet: Invoice Number CC-2026-0168 must have Status 'ON HOLD' and Hold Code 'APPR'.",
|
|
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('ap_ledger.xlsx'))\n if not xlsx_files:\n xlsx_files = list(Path(workspace_path).glob('*.xlsx'))\n xlsx_files = [f for f in xlsx_files if 'ledger' in f.name.lower()]\n if not xlsx_files:\n return {'pass': False, 'score': 0.0, 'feedback': 'ap_ledger.xlsx not found in workspace'}\n \n wb = openpyxl.load_workbook(xlsx_files[0])\n sheet_names = [s.lower() for s in wb.sheetnames]\n invoice_sheet = None\n for name in wb.sheetnames:\n if 'invoice' in name.lower() and 'register' in name.lower():\n invoice_sheet = wb[name]\n break\n if invoice_sheet is None:\n for name in wb.sheetnames:\n if 'invoice' in name.lower():\n invoice_sheet = wb[name]\n break\n if invoice_sheet is None:\n return {'pass': False, 'score': 0.0, 'feedback': 'Invoice Register sheet not found'}\n \n headers = {}\n for col in range(1, invoice_sheet.max_column + 1):\n val = invoice_sheet.cell(row=1, column=col).value\n if val:\n headers[str(val).strip().lower()] = col\n \n invoice_col = None\n status_col = None\n hold_col = None\n for h, c in headers.items():\n if 'invoice' in h and ('number' in h or 'num' in h or 'no' in h or '#' in h):\n invoice_col = c\n elif h == 'invoice number' or h == 'invoice no' or h == 'invoice#':\n invoice_col = c\n if 'status' in h and 'match' not in h:\n status_col = c\n if 'hold' in h and 'code' in h:\n hold_col = c\n \n # Try alternate header matching\n for h, c in headers.items():\n if invoice_col is None and 'invoice' in h:\n invoice_col = c\n if hold_col is None and 'hold' in h:\n hold_col = c\n \n if invoice_col is None or status_col is None:\n return {'pass': False, 'score': 0.0, 'feedback': f'Could not find required columns. Headers found: {list(headers.keys())}'}\n \n for row in range(2, invoice_sheet.max_row + 1):\n inv_val = invoice_sheet.cell(row=row, column=invoice_col).value\n if inv_val and str(inv_val).strip() == 'CC-2026-0168':\n status_val = invoice_sheet.cell(row=row, column=status_col).value\n hold_val = invoice_sheet.cell(row=row, column=hold_col).value if hold_col else None\n status_str = str(status_val).strip().upper() if status_val else ''\n hold_str = str(hold_val).strip().upper() if hold_val else ''\n if 'ON HOLD' in status_str or status_str == 'ON HOLD':\n if hold_str == 'APPR':\n return {'pass': True, 'score': 1.0, 'feedback': f'CC-2026-0168 has Status={status_val}, Hold Code={hold_val} as expected'}\n else:\n return {'pass': False, 'score': 0.0, 'feedback': f'CC-2026-0168 has Status={status_val} but Hold Code={hold_val} (expected APPR)'}\n else:\n return {'pass': False, 'score': 0.0, 'feedback': f'CC-2026-0168 has Status={status_val} (expected ON HOLD), Hold Code={hold_val}'}\n \n return {'pass': False, 'score': 0.0, 'feedback': 'Invoice CC-2026-0168 not found in Invoice Register'}\n",
|
|
"criterion_type": "expected_output"
|
|
},
|
|
{
|
|
"id": "600c2927-4972-436d-9c47-c74f620e9e57",
|
|
"sort_order": 1,
|
|
"rubric_text": "In file `ap_ledger.xlsx`, Payment Queue sheet: Invoice Number CC-2026-0168 must NOT appear.",
|
|
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('ap_ledger.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'ledger' in f.name.lower()]\n if not xlsx_files:\n return {'pass': False, 'score': 0.0, 'feedback': 'ap_ledger.xlsx not found in workspace'}\n \n wb = openpyxl.load_workbook(xlsx_files[0])\n queue_sheet = None\n for name in wb.sheetnames:\n if 'payment' in name.lower() and 'queue' in name.lower():\n queue_sheet = wb[name]\n break\n if queue_sheet is None:\n for name in wb.sheetnames:\n if 'queue' in name.lower():\n queue_sheet = wb[name]\n break\n if queue_sheet is None:\n return {'pass': False, 'score': 0.0, 'feedback': 'Payment Queue sheet not found'}\n \n for row in range(1, queue_sheet.max_row + 1):\n for col in range(1, queue_sheet.max_column + 1):\n val = queue_sheet.cell(row=row, column=col).value\n if val and 'CC-2026-0168' in str(val):\n return {'pass': False, 'score': 0.0, 'feedback': f'CC-2026-0168 found in Payment Queue at row {row} - it should not be there'}\n \n return {'pass': True, 'score': 1.0, 'feedback': 'CC-2026-0168 correctly absent from Payment Queue'}\n",
|
|
"criterion_type": "incorrect_behavior"
|
|
},
|
|
{
|
|
"id": "a8717ac3-48f0-4ae5-96a0-9d0be7a89e67",
|
|
"sort_order": 2,
|
|
"rubric_text": "In mailbox.json, a sent email to t.okonkwo@meridianpartners.com must exist with subject containing 'Cascade Cleaning' and 'CC-2026-0168', and body containing: 'Tanya' or 'Okonkwo', 'A recurring invoice has been received for a vendor whose agreement has expired. The invoice has been placed on APPR hold and cannot be processed until the agreement is renewed or confirmed.', 'Cascade Cleaning', 'V-000031', 'CC-2026-0168', the date April 1 2026, the amount $2,200, and the date March 31 2026.",
|
|
"verifier_code": "from pathlib import Path\nimport json\nimport re\n\ndef verify(workspace_path, external_services_path=None):\n if external_services_path is None:\n return {'pass': False, 'score': 0.0, 'feedback': 'external_services_path not provided; cannot check mailbox'}\n \n mailbox_path = Path(external_services_path) / 'mailbox.json'\n if not mailbox_path.exists():\n return {'pass': False, 'score': 0.0, 'feedback': 'mailbox.json not found'}\n \n with open(mailbox_path) as f:\n data = json.load(f)\n \n emails = data.get('emails', [])\n sent_emails = [e for e in emails if e.get('folder', '').lower() == 'sent']\n \n target_recipient = 't.okonkwo@meridianpartners.com'\n \n matching_emails = []\n for email in sent_emails:\n to_addr = str(email.get('to_addr', '')).lower()\n if target_recipient.lower() in to_addr:\n matching_emails.append(email)\n \n if not matching_emails:\n return {'pass': False, 'score': 0.0, 'feedback': f'No sent email found to {target_recipient}'}\n \n required_subject_terms = ['cascade cleaning', 'cc-2026-0168']\n required_body_terms = [\n 'cascade cleaning',\n 'v-000031',\n 'cc-2026-0168',\n ]\n \n # 'Tanya' or 'Okonkwo' must appear in body\n name_alternatives = ['tanya', 'okonkwo']\n \n # The key sentence from the rubric (check loosely via fragments)\n # \"A recurring invoice has been received for a vendor whose agreement has expired.\n # The invoice has been placed on APPR hold and cannot be processed until the agreement is renewed or confirmed.\"\n key_message_fragments = [\n 'recurring invoice',\n 'agreement',\n 'expired',\n 'hold',\n 'renewed',\n 'confirmed',\n ]\n \n # Amount check - accept $2,200 or 2200 or 2,200 or 2200.00 etc.\n amount_patterns = [r'\\$?2[,.]?200']\n \n # Date terms - accept various formats for April 1 2026 and March 31 2026\n date_patterns_apr1 = [\n r'april\\s*1[,\\s]+2026', r'april\\s*0?1[,\\s]+2026',\n r'apr\\.?\\s*1[,\\s]+2026', r'apr\\.?\\s*0?1[,\\s]+2026',\n r'2026-04-01', r'04/01/2026', r'04-01-2026',\n r'1\\s+april\\s+2026', r'01\\s+april\\s+2026',\n r'april\\s*1\\b', r'apr\\.?\\s*1\\b',\n ]\n date_patterns_mar31 = [\n r'march\\s*31[,\\s]+2026', r'mar\\.?\\s*31[,\\s]+2026',\n r'2026-03-31', r'03/31/2026', r'03-31-2026',\n r'31\\s+march\\s+2026',\n r'march\\s*31\\b', r'mar\\.?\\s*31\\b',\n ]\n \n for email in matching_emails:\n subject = str(email.get('subject', '')).lower()\n body = str(email.get('body_text', '') or email.get('body', '')).lower()\n \n subject_ok = all(term in subject for term in required_subject_terms)\n if not subject_ok:\n continue\n \n body_terms_ok = all(term.lower() in body for term in required_body_terms)\n \n # Check name alternatives: 'Tanya' or 'Okonkwo'\n name_ok = any(name in body for name in name_alternatives)\n \n # Check key message fragments (at least 4 of 6 for leniency)\n msg_hits = sum(1 for frag in key_message_fragments if frag in body)\n msg_ok = msg_hits >= 4\n \n amount_ok = any(re.search(p, body) for p in amount_patterns)\n apr1_ok = any(re.search(p, body) for p in date_patterns_apr1)\n mar31_ok = any(re.search(p, body) for p in date_patterns_mar31)\n \n if body_terms_ok and name_ok and msg_ok and amount_ok and apr1_ok and mar31_ok:\n return {'pass': True, 'score': 1.0, 'feedback': f'Found qualifying sent email to {target_recipient} with all required content about CC-2026-0168'}\n \n # Provide detailed failure info\n for email in matching_emails:\n subject = str(email.get('subject', '')).lower()\n body = str(email.get('body_text', '') or email.get('body', '')).lower()\n missing = []\n for term in required_subject_terms:\n if term not in subject:\n missing.append(f'subject missing: {term}')\n for term in required_body_terms:\n if term.lower() not in body:\n missing.append(f'body missing: {term}')\n if not any(name in body for name in name_alternatives):\n missing.append('body missing: Tanya or Okonkwo')\n missing_frags = [frag for frag in key_message_fragments if frag not in body]\n if len(missing_frags) > 2:\n missing.append(f'body missing key message fragments: {missing_frags}')\n if not any(re.search(p, body) for p in amount_patterns):\n missing.append('body missing: $2,200 amount')\n if not any(re.search(p, body) for p in date_patterns_apr1):\n missing.append('body missing: April 1, 2026 date')\n if not any(re.search(p, body) for p in date_patterns_mar31):\n missing.append('body missing: March 31, 2026 date')\n return {'pass': False, 'score': 0.0, 'feedback': f'Email found to {target_recipient} but missing required content: {missing}. Body preview: {body[:500]}'}\n \n return {'pass': False, 'score': 0.0, 'feedback': f'No sent email to {target_recipient} matched all requirements (subject must contain Cascade Cleaning and CC-2026-0168)'}\n",
|
|
"criterion_type": "expected_output"
|
|
},
|
|
{
|
|
"id": "53e3483a-22d8-405c-9af6-64d7faa8b50c",
|
|
"sort_order": 3,
|
|
"rubric_text": "In file `ap_ledger.xlsx`, Invoice Register sheet: Invoice IDs INV-018 through INV-026 (all nine) must each have Status 'APPROVED' and no hold code.",
|
|
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('ap_ledger.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'ledger' in f.name.lower()]\n if not xlsx_files:\n return {'pass': False, 'score': 0.0, 'feedback': 'ap_ledger.xlsx not found in workspace'}\n \n wb = openpyxl.load_workbook(xlsx_files[0])\n invoice_sheet = None\n for name in wb.sheetnames:\n if 'invoice' in name.lower() and 'register' in name.lower():\n invoice_sheet = wb[name]\n break\n if invoice_sheet is None:\n for name in wb.sheetnames:\n if 'invoice' in name.lower():\n invoice_sheet = wb[name]\n break\n if invoice_sheet is None:\n return {'pass': False, 'score': 0.0, 'feedback': 'Invoice Register sheet not found'}\n \n headers = {}\n for col in range(1, invoice_sheet.max_column + 1):\n val = invoice_sheet.cell(row=1, column=col).value\n if val:\n headers[str(val).strip().lower()] = col\n \n inv_id_col = None\n status_col = None\n hold_col = None\n for h, c in headers.items():\n if h in ('invoice id', 'inv id', 'id', 'invoice_id') or ('invoice' in h and 'id' in h):\n inv_id_col = c\n if 'status' in h and 'match' not in h:\n status_col = c\n if 'hold' in h and 'code' in h:\n hold_col = c\n elif 'hold' in h and hold_col is None:\n hold_col = c\n \n if inv_id_col is None:\n # Try first column as ID\n inv_id_col = 1\n \n if status_col is None:\n return {'pass': False, 'score': 0.0, 'feedback': f'Status column not found. Headers: {list(headers.keys())}'}\n \n required_ids = {f'INV-0{i}' for i in range(18, 27)}\n found = {}\n \n for row in range(2, invoice_sheet.max_row + 1):\n inv_val = invoice_sheet.cell(row=row, column=inv_id_col).value\n if inv_val and str(inv_val).strip() in required_ids:\n inv_id = str(inv_val).strip()\n status_val = invoice_sheet.cell(row=row, column=status_col).value\n hold_val = invoice_sheet.cell(row=row, column=hold_col).value if hold_col else None\n found[inv_id] = {'status': status_val, 'hold': hold_val}\n \n missing = required_ids - set(found.keys())\n if missing:\n return {'pass': False, 'score': 0.0, 'feedback': f'Invoice IDs not found in register: {missing}'}\n \n failures = []\n for inv_id, vals in found.items():\n status_str = str(vals['status']).strip().upper() if vals['status'] else ''\n hold_str = str(vals['hold']).strip() if vals['hold'] else ''\n if status_str != 'APPROVED':\n failures.append(f'{inv_id} has Status={vals[\"status\"]} (expected APPROVED)')\n if hold_str and hold_str.upper() not in ('', 'NONE', 'N/A', 'NULL'):\n failures.append(f'{inv_id} has Hold Code={vals[\"hold\"]} (expected empty)')\n \n if failures:\n return {'pass': False, 'score': 0.0, 'feedback': 'Failures: ' + '; '.join(failures)}\n \n return {'pass': True, 'score': 1.0, 'feedback': f'All 9 invoices INV-018 through INV-026 have Status=APPROVED and no hold code'}\n",
|
|
"criterion_type": "expected_output"
|
|
},
|
|
{
|
|
"id": "860f1841-7c7f-4001-89b3-58b9d47100d0",
|
|
"sort_order": 4,
|
|
"rubric_text": "In file `ap_ledger.xlsx`, Payment Queue sheet: INV-018 through INV-026 must each appear with correct Payment Amt values (INV-018: $620.00, INV-019: $8,750.00, INV-020: $2,800.00, INV-021: $4,500.00, INV-022: $14,000.00, INV-023: $3,200.00, INV-024: $1,050.00, INV-025: $5,500.00, INV-026: $5,200.00).",
|
|
"verifier_code": "from pathlib import Path\nimport openpyxl\nimport re\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('ap_ledger.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'ledger' in f.name.lower()]\n if not xlsx_files:\n return {'pass': False, 'score': 0.0, 'feedback': 'ap_ledger.xlsx not found in workspace'}\n \n wb = openpyxl.load_workbook(xlsx_files[0])\n queue_sheet = None\n for name in wb.sheetnames:\n if 'payment' in name.lower() and 'queue' in name.lower():\n queue_sheet = wb[name]\n break\n if queue_sheet is None:\n for name in wb.sheetnames:\n if 'queue' in name.lower():\n queue_sheet = wb[name]\n break\n if queue_sheet is None:\n return {'pass': False, 'score': 0.0, 'feedback': 'Payment Queue sheet not found'}\n \n expected = {\n 'INV-018': 620.00,\n 'INV-019': 8750.00,\n 'INV-020': 2800.00,\n 'INV-021': 4500.00,\n 'INV-022': 14000.00,\n 'INV-023': 3200.00,\n 'INV-024': 1050.00,\n 'INV-025': 5500.00,\n 'INV-026': 5200.00,\n }\n \n headers = {}\n for col in range(1, queue_sheet.max_column + 1):\n val = queue_sheet.cell(row=1, column=col).value\n if val:\n headers[str(val).strip().lower()] = col\n \n inv_id_col = None\n amt_col = None\n for h, c in headers.items():\n if 'invoice' in h and 'id' in h:\n inv_id_col = c\n elif h in ('inv id', 'invoice id', 'id') and inv_id_col is None:\n inv_id_col = c\n if 'payment' in h and ('amt' in h or 'amount' in h):\n amt_col = c\n elif ('amount' in h or 'amt' in h) and amt_col is None:\n amt_col = c\n \n if inv_id_col is None:\n inv_id_col = 1\n \n if amt_col is None:\n return {'pass': False, 'score': 0.0, 'feedback': f'Payment amount column not found. Headers: {list(headers.keys())}'}\n \n def parse_amount(val):\n if val is None:\n return None\n if isinstance(val, (int, float)):\n return float(val)\n s = str(val).replace('$', '').replace(',', '').strip()\n try:\n return float(s)\n except:\n return None\n \n found = {}\n for row in range(2, queue_sheet.max_row + 1):\n inv_val = queue_sheet.cell(row=row, column=inv_id_col).value\n if inv_val and str(inv_val).strip() in expected:\n inv_id = str(inv_val).strip()\n amt_val = queue_sheet.cell(row=row, column=amt_col).value\n found[inv_id] = parse_amount(amt_val)\n \n missing = set(expected.keys()) - set(found.keys())\n if missing:\n return {'pass': False, 'score': 0.0, 'feedback': f'Invoice IDs missing from Payment Queue: {missing}'}\n \n failures = []\n for inv_id, exp_amt in expected.items():\n actual = found.get(inv_id)\n if actual is None:\n failures.append(f'{inv_id}: could not parse amount')\n else:\n tol = exp_amt * 0.01\n if abs(actual - exp_amt) > tol:\n failures.append(f'{inv_id}: expected {exp_amt}, got {actual}')\n \n if failures:\n return {'pass': False, 'score': 0.0, 'feedback': 'Amount mismatches: ' + '; '.join(failures)}\n \n return {'pass': True, 'score': 1.0, 'feedback': f'All 9 invoices INV-018 through INV-026 found in Payment Queue with correct amounts'}\n",
|
|
"criterion_type": "expected_output"
|
|
},
|
|
{
|
|
"id": "7b93f221-bd0e-470f-b332-715f3d44329b",
|
|
"sort_order": 5,
|
|
"rubric_text": "In file `ap_ledger.xlsx`, Invoice Register sheet: INV-007 must have Status 'ON HOLD' with Hold Code 'MATCH'; INV-008 must have Status 'ON HOLD' with Hold Code 'APPR'; INV-009 must have Status 'ON HOLD' with Hold Code 'MATCH'; INV-011 must have Status 'IC-HOLD'.",
|
|
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('ap_ledger.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'ledger' in f.name.lower()]\n if not xlsx_files:\n return {'pass': False, 'score': 0.0, 'feedback': 'ap_ledger.xlsx not found in workspace'}\n \n wb = openpyxl.load_workbook(xlsx_files[0])\n invoice_sheet = None\n for name in wb.sheetnames:\n if 'invoice' in name.lower() and 'register' in name.lower():\n invoice_sheet = wb[name]\n break\n if invoice_sheet is None:\n for name in wb.sheetnames:\n if 'invoice' in name.lower():\n invoice_sheet = wb[name]\n break\n if invoice_sheet is None:\n return {'pass': False, 'score': 0.0, 'feedback': 'Invoice Register sheet not found'}\n \n headers = {}\n for col in range(1, invoice_sheet.max_column + 1):\n val = invoice_sheet.cell(row=1, column=col).value\n if val:\n headers[str(val).strip().lower()] = col\n \n inv_id_col = None\n status_col = None\n hold_col = None\n for h, c in headers.items():\n if 'invoice' in h and 'id' in h:\n inv_id_col = c\n elif h in ('inv id', 'id') and inv_id_col is None:\n inv_id_col = c\n if 'status' in h and 'match' not in h:\n status_col = c\n if 'hold' in h and 'code' in h:\n hold_col = c\n elif 'hold' in h and hold_col is None:\n hold_col = c\n \n if inv_id_col is None:\n inv_id_col = 1\n \n if status_col is None:\n return {'pass': False, 'score': 0.0, 'feedback': f'Status column not found. Headers: {list(headers.keys())}'}\n \n expected = {\n 'INV-007': {'status': 'ON HOLD', 'hold': 'MATCH'},\n 'INV-008': {'status': 'ON HOLD', 'hold': 'APPR'},\n 'INV-009': {'status': 'ON HOLD', 'hold': 'MATCH'},\n 'INV-011': {'status': 'IC-HOLD', 'hold': None},\n }\n \n found = {}\n for row in range(2, invoice_sheet.max_row + 1):\n inv_val = invoice_sheet.cell(row=row, column=inv_id_col).value\n if inv_val and str(inv_val).strip() in expected:\n inv_id = str(inv_val).strip()\n status_val = invoice_sheet.cell(row=row, column=status_col).value\n hold_val = invoice_sheet.cell(row=row, column=hold_col).value if hold_col else None\n found[inv_id] = {'status': status_val, 'hold': hold_val}\n \n missing = set(expected.keys()) - set(found.keys())\n if missing:\n return {'pass': False, 'score': 0.0, 'feedback': f'Invoice IDs not found: {missing}'}\n \n failures = []\n for inv_id, exp in expected.items():\n actual = found[inv_id]\n actual_status = str(actual['status']).strip().upper() if actual['status'] else ''\n actual_hold = str(actual['hold']).strip().upper() if actual['hold'] else ''\n exp_status = exp['status'].upper()\n \n if actual_status != exp_status:\n failures.append(f'{inv_id}: Status={actual[\"status\"]} (expected {exp[\"status\"]})')\n \n if exp['hold'] is not None:\n if actual_hold != exp['hold'].upper():\n failures.append(f'{inv_id}: Hold Code={actual[\"hold\"]} (expected {exp[\"hold\"]})')\n \n if failures:\n return {'pass': False, 'score': 0.0, 'feedback': 'Failures: ' + '; '.join(failures)}\n \n return {'pass': True, 'score': 1.0, 'feedback': 'INV-007, INV-008, INV-009, INV-011 all have correct Status and Hold Code values'}\n",
|
|
"criterion_type": "incorrect_behavior"
|
|
},
|
|
{
|
|
"id": "9519c1fd-5be9-4de0-8f31-3d1006cd104a",
|
|
"sort_order": 6,
|
|
"rubric_text": "In file `ap_ledger.xlsx`, Payment Queue sheet: Invoice IDs INV-007, INV-008, INV-009, and INV-011 must NOT appear.",
|
|
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('ap_ledger.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'ledger' in f.name.lower()]\n if not xlsx_files:\n return {'pass': False, 'score': 0.0, 'feedback': 'ap_ledger.xlsx not found in workspace'}\n \n wb = openpyxl.load_workbook(xlsx_files[0])\n queue_sheet = None\n for name in wb.sheetnames:\n if 'payment' in name.lower() and 'queue' in name.lower():\n queue_sheet = wb[name]\n break\n if queue_sheet is None:\n for name in wb.sheetnames:\n if 'queue' in name.lower():\n queue_sheet = wb[name]\n break\n if queue_sheet is None:\n return {'pass': False, 'score': 0.0, 'feedback': 'Payment Queue sheet not found'}\n \n forbidden = {'INV-007', 'INV-008', 'INV-009', 'INV-011'}\n found_forbidden = []\n \n for row in range(1, queue_sheet.max_row + 1):\n for col in range(1, queue_sheet.max_column + 1):\n val = queue_sheet.cell(row=row, column=col).value\n if val and str(val).strip() in forbidden:\n found_forbidden.append(f'{str(val).strip()} at row {row}')\n \n if found_forbidden:\n return {'pass': False, 'score': 0.0, 'feedback': f'Forbidden Invoice IDs found in Payment Queue: {found_forbidden}'}\n \n return {'pass': True, 'score': 1.0, 'feedback': 'INV-007, INV-008, INV-009, INV-011 correctly absent from Payment Queue'}\n",
|
|
"criterion_type": "incorrect_behavior"
|
|
},
|
|
{
|
|
"id": "323b236e-507a-4454-8c71-e027177ad6c4",
|
|
"sort_order": 7,
|
|
"rubric_text": "In file `match_log_2026_Q2.xlsx`, Invoice IDs INV-018, INV-021, INV-024, and INV-026 must each appear with Match Date 2026-04-08 and Match Result containing the word \"PASS\".",
|
|
"verifier_code": "from pathlib import Path\nimport openpyxl\nimport re\nfrom datetime import datetime, date\n\ndef verify(workspace_path, external_services_path=None):\n # Try to find the match log file with flexible matching\n xlsx_files = list(Path(workspace_path).glob('match_log_2026_Q2.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'match' in f.name.lower() and 'log' in f.name.lower()]\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'match' in f.name.lower()]\n if not xlsx_files:\n # Also search subdirectories\n xlsx_files = list(Path(workspace_path).rglob('match_log_2026_Q2.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).rglob('*.xlsx') if 'match' in f.name.lower() and 'log' in f.name.lower()]\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).rglob('*.xlsx') if 'match' in f.name.lower()]\n if not xlsx_files:\n return {'pass': False, 'score': 0.0, 'feedback': 'match_log_2026_Q2.xlsx not found in workspace'}\n \n wb = openpyxl.load_workbook(xlsx_files[0])\n ws = wb.active\n if len(wb.sheetnames) > 1:\n for name in wb.sheetnames:\n if 'match' in name.lower() or 'log' in name.lower() or '2026' in name:\n ws = wb[name]\n break\n \n # Parse headers flexibly\n headers = {}\n for col in range(1, ws.max_column + 1):\n val = ws.cell(row=1, column=col).value\n if val:\n headers[str(val).strip().lower()] = col\n \n inv_id_col = None\n date_col = None\n result_col = None\n \n for h, c in headers.items():\n if 'invoice' in h and 'id' in h:\n inv_id_col = c\n elif h in ('inv id', 'id', 'invoice_id', 'inv_id', 'invoiceid') and inv_id_col is None:\n inv_id_col = c\n if 'match' in h and 'date' in h:\n date_col = c\n elif 'date' in h and date_col is None:\n date_col = c\n if 'match' in h and ('result' in h or 'status' in h):\n result_col = c\n elif ('result' in h or 'status' in h) and result_col is None:\n result_col = c\n \n if inv_id_col is None:\n inv_id_col = 1\n \n if date_col is None or result_col is None:\n return {'pass': False, 'score': 0.0, 'feedback': f'Required columns not found. Headers found: {list(headers.keys())}'}\n \n # The rubric requires these four invoices with Match Date 2026-04-08 and Match Result containing \"PASS\"\n expected_invoices = {'INV-018', 'INV-021', 'INV-024', 'INV-026'}\n \n found = {}\n for row in range(2, ws.max_row + 1):\n inv_val = ws.cell(row=row, column=inv_id_col).value\n if inv_val and str(inv_val).strip() in expected_invoices:\n inv_id = str(inv_val).strip()\n date_val = ws.cell(row=row, column=date_col).value\n result_val = ws.cell(row=row, column=result_col).value\n found[inv_id] = {'date': date_val, 'result': result_val, 'row': row}\n \n missing = expected_invoices - set(found.keys())\n if missing:\n return {'pass': False, 'score': 0.0, 'feedback': f'Invoice IDs missing from match log: {sorted(missing)}'}\n \n def check_date(val):\n if val is None:\n return False\n if isinstance(val, datetime):\n return val.date() == date(2026, 4, 8)\n if isinstance(val, date):\n return val == date(2026, 4, 8)\n s = str(val).strip()\n return ('2026-04-08' in s or '04/08/2026' in s or '04-08-2026' in s or\n '2026/04/08' in s or 'april 8' in s.lower() or 'apr 8' in s.lower() or\n '8 apr' in s.lower() or '8 april' in s.lower() or\n '4/8/2026' in s or '8/4/2026' in s or '2026.04.08' in s)\n \n def check_result_contains_pass(val):\n \"\"\"Rubric says Match Result must contain the word PASS.\"\"\"\n if val is None:\n return False\n result_str = str(val).strip().upper()\n # Check if the word PASS appears anywhere in the result string\n # This is intentionally loose: PASS, MATCH-PASS, MATCH_PASS, MATCH PASS, 3-Way Pass, etc.\n return 'PASS' in result_str\n \n failures = []\n for inv_id in sorted(expected_invoices):\n actual = found[inv_id]\n if not check_date(actual['date']):\n failures.append(f'{inv_id} (row {actual[\"row\"]}): Match Date={actual[\"date\"]} (expected 2026-04-08)')\n if not check_result_contains_pass(actual['result']):\n failures.append(f'{inv_id} (row {actual[\"row\"]}): Match Result={actual[\"result\"]} (expected to contain the word PASS)')\n \n if failures:\n return {'pass': False, 'score': 0.0, 'feedback': 'Match log failures: ' + '; '.join(failures)}\n \n # Collect actual values for detailed feedback\n details = []\n for inv_id in sorted(expected_invoices):\n actual = found[inv_id]\n details.append(f'{inv_id}: date={actual[\"date\"]}, result={actual[\"result\"]}')\n \n return {'pass': True, 'score': 1.0, 'feedback': f'All required invoices (INV-018, INV-021, INV-024, INV-026) found in match log with Match Date 2026-04-08 and Match Result containing PASS. Details: ' + '; '.join(details)}\n",
|
|
"criterion_type": "expected_output"
|
|
}
|
|
]
|