Add Toast __OO_STATE__ fast-path for URL-fetched menu pages
Instead of sending 450KB of HTML to Claude (which truncates to 100K and only extracts ~60 items), parse the structured __OO_STATE__ data directly on the server. This captures all menus, groups, items, prices, and images from Toast pages - 169 items for Jus Family Cafe vs 60 before. Falls back to Claude analysis if __OO_STATE__ parsing fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ffedc26150
commit
a0d86d6e87
1 changed files with 217 additions and 0 deletions
|
|
@ -934,6 +934,223 @@
|
|||
|
||||
<cfset arrayAppend(response.steps, "Loaded #arrayLen(imageDataArray)# valid images (#localReadCount# from local disk)")>
|
||||
|
||||
<!--- ============================================================ --->
|
||||
<!--- TOAST FAST PATH: Parse __OO_STATE__ directly instead of Claude --->
|
||||
<!--- ============================================================ --->
|
||||
<cfif findNoCase("window.__OO_STATE__", pageHtml) AND findNoCase("toasttab", pageHtml)>
|
||||
<cfset arrayAppend(response.steps, "Toast page detected - extracting menu data from __OO_STATE__")>
|
||||
<cftry>
|
||||
<cfset ooStateMatch = reMatchNoCase("window\.__OO_STATE__\s*=\s*(\{.*?\});\s*window\.", pageHtml)>
|
||||
<cfif arrayLen(ooStateMatch)>
|
||||
<cfset ooStateJson = reReplaceNoCase(ooStateMatch[1], "window\.__OO_STATE__\s*=\s*", "")>
|
||||
<cfset ooStateJson = reReplace(ooStateJson, ";\s*window\.$", "")>
|
||||
<cfset ooState = deserializeJSON(ooStateJson)>
|
||||
|
||||
<cfset toastBusiness = structNew()>
|
||||
<cfset toastCategories = arrayNew(1)>
|
||||
<cfset toastItems = arrayNew(1)>
|
||||
<cfset categorySet = structNew()>
|
||||
<cfset itemId = 1>
|
||||
<cfset menuNames = arrayNew(1)>
|
||||
|
||||
<cfloop collection="#ooState#" item="ooKey">
|
||||
<!--- Extract restaurant info --->
|
||||
<cfif left(ooKey, 11) EQ "Restaurant:">
|
||||
<cfset restaurant = ooState[ooKey]>
|
||||
<cfif structKeyExists(restaurant, "name")>
|
||||
<cfset toastBusiness["name"] = restaurant.name>
|
||||
</cfif>
|
||||
<cfif structKeyExists(restaurant, "location")>
|
||||
<cfset loc = restaurant.location>
|
||||
<cfif structKeyExists(loc, "address1")>
|
||||
<cfset toastBusiness["address"] = loc.address1>
|
||||
<cfif structKeyExists(loc, "city")><cfset toastBusiness["address"] = toastBusiness.address & ", " & loc.city></cfif>
|
||||
<cfif structKeyExists(loc, "state")><cfset toastBusiness["address"] = toastBusiness.address & ", " & loc.state></cfif>
|
||||
<cfif structKeyExists(loc, "zipCode")><cfset toastBusiness["address"] = toastBusiness.address & " " & loc.zipCode></cfif>
|
||||
</cfif>
|
||||
<cfif structKeyExists(loc, "phone")>
|
||||
<cfset toastBusiness["phone"] = loc.phone>
|
||||
</cfif>
|
||||
</cfif>
|
||||
<cfif structKeyExists(restaurant, "brandColor")>
|
||||
<cfset toastBusiness["brandColor"] = replace(restaurant.brandColor, "##", "")>
|
||||
</cfif>
|
||||
</cfif>
|
||||
|
||||
<!--- Extract menu data --->
|
||||
<cfif left(ooKey, 5) EQ "Menu:">
|
||||
<cfset menu = ooState[ooKey]>
|
||||
<cfif structKeyExists(menu, "groups") AND isArray(menu.groups)>
|
||||
<!--- Use menu name as parent category if multiple menus --->
|
||||
<cfset menuName = structKeyExists(menu, "name") ? menu.name : "">
|
||||
<cfif len(menuName)><cfset arrayAppend(menuNames, menuName)></cfif>
|
||||
<cfset isMultiMenu = structCount(ooState) GT 0><!--- will check later --->
|
||||
|
||||
<cfloop array="#menu.groups#" index="group">
|
||||
<cfset groupName = structKeyExists(group, "name") ? trim(group.name) : "Menu">
|
||||
<cfif NOT structKeyExists(categorySet, groupName)>
|
||||
<cfset categorySet[groupName] = true>
|
||||
<cfset catObj = { "name": groupName, "itemCount": 0 }>
|
||||
<cfset arrayAppend(toastCategories, catObj)>
|
||||
</cfif>
|
||||
|
||||
<!--- Extract items from group --->
|
||||
<cfif structKeyExists(group, "items") AND isArray(group.items)>
|
||||
<cfloop array="#group.items#" index="item">
|
||||
<cfif structKeyExists(item, "name") AND len(trim(item.name))>
|
||||
<cfset itemStruct = structNew()>
|
||||
<cfset itemStruct["id"] = "item_" & itemId>
|
||||
<cfset itemStruct["name"] = trim(item.name)>
|
||||
<cfset itemStruct["category"] = groupName>
|
||||
<cfset itemStruct["modifiers"] = arrayNew(1)>
|
||||
<cfset itemStruct["description"] = "">
|
||||
<cfif structKeyExists(item, "description") AND NOT isNull(item.description)>
|
||||
<cfset itemStruct["description"] = trim(toString(item.description))>
|
||||
</cfif>
|
||||
|
||||
<!--- Extract price --->
|
||||
<cfset itemStruct["price"] = 0>
|
||||
<cfif structKeyExists(item, "price") AND isNumeric(item.price)>
|
||||
<cfset itemStruct["price"] = val(item.price)>
|
||||
<cfelseif structKeyExists(item, "unitPrice") AND isNumeric(item.unitPrice)>
|
||||
<cfset itemStruct["price"] = val(item.unitPrice)>
|
||||
<cfelseif structKeyExists(item, "basePrice") AND isNumeric(item.basePrice)>
|
||||
<cfset itemStruct["price"] = val(item.basePrice)>
|
||||
<cfelseif structKeyExists(item, "displayPrice") AND len(trim(toString(item.displayPrice)))>
|
||||
<cfset priceStr = reReplace(toString(item.displayPrice), "[^0-9.]", "", "all")>
|
||||
<cfif len(priceStr) AND isNumeric(priceStr)>
|
||||
<cfset itemStruct["price"] = val(priceStr)>
|
||||
</cfif>
|
||||
</cfif>
|
||||
|
||||
<!--- Extract image URL --->
|
||||
<cfset itemStruct["imageUrl"] = "">
|
||||
<cfif structKeyExists(item, "imageUrls") AND isStruct(item.imageUrls)>
|
||||
<cfif structKeyExists(item.imageUrls, "medium")>
|
||||
<cfset itemStruct["imageUrl"] = item.imageUrls.medium>
|
||||
<cfelseif structKeyExists(item.imageUrls, "large")>
|
||||
<cfset itemStruct["imageUrl"] = item.imageUrls.large>
|
||||
<cfelseif structKeyExists(item.imageUrls, "small")>
|
||||
<cfset itemStruct["imageUrl"] = item.imageUrls.small>
|
||||
</cfif>
|
||||
<cfif len(itemStruct.imageUrl)>
|
||||
<cfset itemStruct["imageSrc"] = itemStruct.imageUrl>
|
||||
<cfset itemStruct["imageFilename"] = listLast(itemStruct.imageUrl, "/")>
|
||||
</cfif>
|
||||
</cfif>
|
||||
|
||||
<cfset arrayAppend(toastItems, itemStruct)>
|
||||
<cfset itemId++>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
</cfif>
|
||||
|
||||
<!--- Extract items from subgroups --->
|
||||
<cfset subgroupsArr = arrayNew(1)>
|
||||
<cfif structKeyExists(group, "subgroups") AND isArray(group.subgroups)>
|
||||
<cfset subgroupsArr = group.subgroups>
|
||||
<cfelseif structKeyExists(group, "children") AND isArray(group.children)>
|
||||
<cfset subgroupsArr = group.children>
|
||||
<cfelseif structKeyExists(group, "childGroups") AND isArray(group.childGroups)>
|
||||
<cfset subgroupsArr = group.childGroups>
|
||||
</cfif>
|
||||
<cfloop array="#subgroupsArr#" index="subgroup">
|
||||
<cfset subName = structKeyExists(subgroup, "name") ? trim(subgroup.name) : groupName>
|
||||
<cfif len(subName) AND NOT structKeyExists(categorySet, subName)>
|
||||
<cfset categorySet[subName] = true>
|
||||
<cfset arrayAppend(toastCategories, { "name": subName, "parentCategoryName": groupName, "itemCount": 0 })>
|
||||
</cfif>
|
||||
<cfif structKeyExists(subgroup, "items") AND isArray(subgroup.items)>
|
||||
<cfloop array="#subgroup.items#" index="subItem">
|
||||
<cfif structKeyExists(subItem, "name") AND len(trim(subItem.name))>
|
||||
<cfset itemStruct = structNew()>
|
||||
<cfset itemStruct["id"] = "item_" & itemId>
|
||||
<cfset itemStruct["name"] = trim(subItem.name)>
|
||||
<cfset itemStruct["category"] = subName>
|
||||
<cfset itemStruct["modifiers"] = arrayNew(1)>
|
||||
<cfset itemStruct["description"] = "">
|
||||
<cfif structKeyExists(subItem, "description") AND NOT isNull(subItem.description)>
|
||||
<cfset itemStruct["description"] = trim(toString(subItem.description))>
|
||||
</cfif>
|
||||
<cfset itemStruct["price"] = 0>
|
||||
<cfif structKeyExists(subItem, "price") AND isNumeric(subItem.price)>
|
||||
<cfset itemStruct["price"] = val(subItem.price)>
|
||||
<cfelseif structKeyExists(subItem, "unitPrice") AND isNumeric(subItem.unitPrice)>
|
||||
<cfset itemStruct["price"] = val(subItem.unitPrice)>
|
||||
<cfelseif structKeyExists(subItem, "basePrice") AND isNumeric(subItem.basePrice)>
|
||||
<cfset itemStruct["price"] = val(subItem.basePrice)>
|
||||
<cfelseif structKeyExists(subItem, "displayPrice") AND len(trim(toString(subItem.displayPrice)))>
|
||||
<cfset priceStr = reReplace(toString(subItem.displayPrice), "[^0-9.]", "", "all")>
|
||||
<cfif len(priceStr) AND isNumeric(priceStr)>
|
||||
<cfset itemStruct["price"] = val(priceStr)>
|
||||
</cfif>
|
||||
</cfif>
|
||||
<cfset itemStruct["imageUrl"] = "">
|
||||
<cfif structKeyExists(subItem, "imageUrls") AND isStruct(subItem.imageUrls)>
|
||||
<cfif structKeyExists(subItem.imageUrls, "medium")>
|
||||
<cfset itemStruct["imageUrl"] = subItem.imageUrls.medium>
|
||||
<cfelseif structKeyExists(subItem.imageUrls, "large")>
|
||||
<cfset itemStruct["imageUrl"] = subItem.imageUrls.large>
|
||||
<cfelseif structKeyExists(subItem.imageUrls, "small")>
|
||||
<cfset itemStruct["imageUrl"] = subItem.imageUrls.small>
|
||||
</cfif>
|
||||
<cfif len(itemStruct.imageUrl)>
|
||||
<cfset itemStruct["imageSrc"] = itemStruct.imageUrl>
|
||||
<cfset itemStruct["imageFilename"] = listLast(itemStruct.imageUrl, "/")>
|
||||
</cfif>
|
||||
</cfif>
|
||||
<cfset arrayAppend(toastItems, itemStruct)>
|
||||
<cfset itemId++>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
</cfloop>
|
||||
</cfif>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
|
||||
<!--- Update category item counts --->
|
||||
<cfloop from="1" to="#arrayLen(toastCategories)#" index="ci">
|
||||
<cfset catName = toastCategories[ci].name>
|
||||
<cfset count = 0>
|
||||
<cfloop array="#toastItems#" index="ti">
|
||||
<cfif ti.category EQ catName><cfset count++></cfif>
|
||||
</cfloop>
|
||||
<cfset toastCategories[ci]["itemCount"] = count>
|
||||
</cfloop>
|
||||
|
||||
<cfset arrayAppend(response.steps, "Extracted " & arrayLen(toastItems) & " items from " & arrayLen(toastCategories) & " categories via __OO_STATE__")>
|
||||
|
||||
<!--- Build and return response directly - skip Claude --->
|
||||
<cfif arrayLen(toastItems) GT 0>
|
||||
<cfset menuData = structNew()>
|
||||
<cfset menuData["business"] = toastBusiness>
|
||||
<cfset menuData["categories"] = toastCategories>
|
||||
<cfset menuData["items"] = toastItems>
|
||||
<cfset menuData["modifiers"] = arrayNew(1)>
|
||||
<cfset menuData["imageUrls"] = arrayNew(1)>
|
||||
<cfset menuData["imageMappings"] = imageMappings>
|
||||
<cfset menuData["headerCandidateIndices"] = arrayNew(1)>
|
||||
|
||||
<cfset response["OK"] = true>
|
||||
<cfset response["DATA"] = menuData>
|
||||
<cfset response["sourceUrl"] = isDefined("targetUrl") ? targetUrl : "uploaded">
|
||||
<cfset response["pagesProcessed"] = 1>
|
||||
<cfset response["imagesFound"] = arrayLen(imageDataArray)>
|
||||
<cfset response["playwrightImagesCount"] = arrayLen(playwrightImages)>
|
||||
<cfset response["parsedVia"] = "toast_oo_state">
|
||||
<cfcontent type="application/json" reset="true">
|
||||
<cfoutput>#serializeJSON(response)#</cfoutput>
|
||||
<cfabort>
|
||||
</cfif>
|
||||
</cfif>
|
||||
<cfcatch>
|
||||
<cfset arrayAppend(response.steps, "Toast __OO_STATE__ parsing failed: " & cfcatch.message & " - falling back to Claude")>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
</cfif>
|
||||
|
||||
<!--- Look for embedded JSON data (Next.js __NEXT_DATA__, Toast state, etc.) --->
|
||||
<cfset embeddedJsonData = "">
|
||||
<cfset embeddedMenuItems = arrayNew(1)>
|
||||
|
|
|
|||
Reference in a new issue