Analyze images in ZIP for business info

- Scan extracted ZIP for image files (jpg, png, gif, webp)
- Skip small files (<10KB, likely icons) and _files folder assets
- Send up to 3 images to Claude for business info extraction
- Merge extracted name, address, phone, hours, brandColor
- Only fills in fields not already found from HTML

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-02-13 10:16:18 -08:00
parent cf34636879
commit f9bfbc8960

View file

@ -505,6 +505,119 @@
</cftry>
<cfset arrayAppend(response.steps, "Extracted " & arrayLen(toastItems) & " unique items from " & arrayLen(toastCategories) & " categories")>
<!--- Scan for images in ZIP and analyze them for business info --->
<cftry>
<cfdirectory action="list" directory="#extractDir#" name="zipImages" recurse="true" type="file">
<cfset imageExtensions = "jpg,jpeg,png,gif,webp">
<cfset zipImageFiles = []>
<cfloop query="zipImages">
<cfset imgExt = lCase(listLast(zipImages.name, "."))>
<cfif listFindNoCase(imageExtensions, imgExt)>
<!--- Skip small files (likely icons) and _files folder assets --->
<cfif zipImages.size GT 10000 AND NOT findNoCase("_files", zipImages.directory)>
<cfset arrayAppend(zipImageFiles, "#zipImages.directory#/#zipImages.name#")>
</cfif>
</cfif>
</cfloop>
<cfif arrayLen(zipImageFiles) GT 0>
<cfset arrayAppend(response.steps, "Found " & arrayLen(zipImageFiles) & " images in ZIP to analyze for business info")>
<!--- Analyze up to 3 images for business info --->
<cfset imgLimit = min(arrayLen(zipImageFiles), 3)>
<cfloop from="1" to="#imgLimit#" index="imgIdx">
<cfset imgPath = zipImageFiles[imgIdx]>
<cftry>
<cfset imgContent = fileReadBinary(imgPath)>
<cfset imgExt = lCase(listLast(imgPath, "."))>
<cfset mediaType = "image/jpeg">
<cfif imgExt EQ "png"><cfset mediaType = "image/png">
<cfelseif imgExt EQ "gif"><cfset mediaType = "image/gif">
<cfelseif imgExt EQ "webp"><cfset mediaType = "image/webp">
</cfif>
<cfset base64Img = toBase64(imgContent)>
<cfset arrayAppend(response.steps, "Analyzing image: " & listLast(imgPath, "/\"))>
<!--- Build Claude request for business info extraction --->
<cfset imgMsgContent = []>
<cfset arrayAppend(imgMsgContent, {
"type": "image",
"source": {
"type": "base64",
"media_type": mediaType,
"data": base64Img
}
})>
<cfset arrayAppend(imgMsgContent, {
"type": "text",
"text": "Extract business information from this image. Return JSON with: name, addressLine1, city, state, zip, phone, hours, brandColor (6-digit hex without #). Only include fields you can see. Return ONLY valid JSON, no markdown."
})>
<cfset imgRequest = {
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"temperature": 0,
"messages": [{
"role": "user",
"content": imgMsgContent
}]
}>
<cfhttp url="https://api.anthropic.com/v1/messages" method="POST" timeout="60" result="imgHttpResult">
<cfhttpparam type="header" name="Content-Type" value="application/json">
<cfhttpparam type="header" name="x-api-key" value="#CLAUDE_API_KEY#">
<cfhttpparam type="header" name="anthropic-version" value="2023-06-01">
<cfhttpparam type="body" value="#serializeJSON(imgRequest)#">
</cfhttp>
<cfif findNoCase("200", imgHttpResult.statusCode)>
<cfset imgResponse = deserializeJSON(imgHttpResult.fileContent)>
<cfif structKeyExists(imgResponse, "content") AND arrayLen(imgResponse.content)>
<cfset imgText = imgResponse.content[1].text>
<!--- Clean up JSON --->
<cfset imgText = trim(imgText)>
<cfif left(imgText, 7) EQ "```json">
<cfset imgText = mid(imgText, 8, len(imgText) - 7)>
</cfif>
<cfif left(imgText, 3) EQ "```">
<cfset imgText = mid(imgText, 4, len(imgText) - 3)>
</cfif>
<cfif right(imgText, 3) EQ "```">
<cfset imgText = left(imgText, len(imgText) - 3)>
</cfif>
<cfset imgText = trim(imgText)>
<cftry>
<cfset imgBizData = deserializeJSON(imgText)>
<!--- Merge into toastBusiness (only fill in missing fields) --->
<cfset bizFieldsToCheck = "name,addressLine1,city,state,zip,phone,hours,brandColor">
<cfloop list="#bizFieldsToCheck#" index="bizField">
<cfif structKeyExists(imgBizData, bizField) AND isSimpleValue(imgBizData[bizField]) AND len(trim(imgBizData[bizField]))>
<cfif NOT structKeyExists(toastBusiness, bizField) OR NOT len(toastBusiness[bizField])>
<cfset toastBusiness[bizField] = trim(imgBizData[bizField])>
<cfset arrayAppend(response.steps, "Found " & bizField & " from image: " & left(toastBusiness[bizField], 50))>
</cfif>
</cfif>
</cfloop>
<cfcatch>
<cfset arrayAppend(response.steps, "Could not parse image analysis JSON")>
</cfcatch>
</cftry>
</cfif>
</cfif>
<cfcatch>
<cfset arrayAppend(response.steps, "Error analyzing image: " & cfcatch.message)>
</cfcatch>
</cftry>
</cfloop>
</cfif>
<cfcatch>
<cfset arrayAppend(response.steps, "Could not scan ZIP for images: " & cfcatch.message)>
</cfcatch>
</cftry>
<!--- Summary of business info found --->
<cfset bizKeys = structKeyList(toastBusiness)>
<cfset arrayAppend(response.steps, "Business info keys: " & (len(bizKeys) ? bizKeys : "(none)"))>