Add meta feed functionality and enhance error handling in catalog API

This commit is contained in:
“VeLiTi”
2025-12-03 11:30:01 +01:00
parent 04b9814c07
commit 990f270855
6 changed files with 336 additions and 8 deletions

256
assets/functions_meta.php Normal file
View File

@@ -0,0 +1,256 @@
<?php
//------------------------------------------
// Function: Transform Catalog to Meta Feed
//------------------------------------------
function catalogToMetaFeed($catalog, $config) {
$meta_feed = [];
foreach ($catalog as $product) {
// Extract product data
$product_id = $product['productcode'] ?? '';
$product_name = $product['productname'] ?? '';
$original_description = cleanText($product['productdescription'] ?? '');
// Get meta description based on product code (with fallback to original)
$description = getMetaDescription($product_id, $original_description);
$base_price = floatval($product['price'] ?? 0);
$url_slug = $product['url_slug'] ?? '';
$main_image = buildImageUrl($product['full_path'] ?? '', $config['image_base_url']);
$product_url = buildProductUrl($url_slug, $config['base_url']);
// Check if product has variants
$has_variants = !empty($product['configurations']);
if (!$has_variants) {
// Simple product - no variants
$meta_feed[] = buildMetaProduct([
'id' => $product_id,
'title' => $product_name,
'description' => $description,
'price' => $base_price,
'link' => $product_url,
'image_link' => $main_image,
], $config);
} else {
// Product with variants
$variants = extractVariants($product, $base_price);
foreach ($variants as $variant) {
// Combine meta description with variant suffix
$variant_description = $description . ' ' . $variant['description_suffix'];
$meta_feed[] = buildMetaProduct([
'id' => $variant['id'],
'item_group_id' => $product_id,
'title' => $variant['title'],
'description' => $variant_description,
'price' => $variant['price'],
'link' => $product_url,
'image_link' => $variant['image'],
'additional_image_link' => $variant['additional_image'] ?? '',
], $config);
}
}
}
return $meta_feed;
}
//------------------------------------------
// Extract variants from configurations
//------------------------------------------
function extractVariants($product, $base_price) {
$variants = [];
$product_id = $product['productcode'];
$product_name = $product['productname'];
$image_base_url = $GLOBALS['meta_config']['image_base_url'];
$configurations = $product['configurations'] ?? [];
foreach ($configurations as $config) {
if ($config['type'] === 'group') {
$group_name = $config['assignment_name'] ?? 'Option';
$attributes = $config['attributes'] ?? [];
foreach ($attributes as $attr) {
// Calculate price
$attr_price = floatval($attr['price'] ?? 0);
$attr_modifier = intval($attr['price_modifier'] ?? 1);
$final_price = calculateVariantPrice($base_price, $attr_price, $attr_modifier);
// Get images (using image_base_url for images)
$variant_image = buildImageUrl($attr['alternative_media_full_path'] ?? '', $image_base_url);
$additional_image = buildImageUrl($attr['full_path'] ?? '', $image_base_url);
// Fallback to main product image if no variant image
if (empty($variant_image)) {
$variant_image = buildImageUrl($product['full_path'] ?? '', $image_base_url);
}
// Extract option name from item_name
$item_name = $attr['item_name'] ?? '';
$option_name = formatAttributeName($item_name);
// Build variant
$variants[] = [
'id' => $product_id . '_' . ($attr['attribute_id'] ?? ''),
'title' => $product_name . ' - ' . $option_name,
'description_suffix' => 'Available with ' . $option_name . ' ' . strtolower($group_name) . '.',
'price' => $final_price,
'image' => $variant_image,
'additional_image' => $additional_image,
];
}
}
}
return $variants;
}
//------------------------------------------
// Build Meta product row
//------------------------------------------
function buildMetaProduct($data, $config) {
$product = [
'id' => $data['id'],
'title' => $data['title'],
'description' => $data['description'],
'availability' => $config['availability'],
'condition' => $config['condition'],
'price' => formatPrice($data['price'], $config['currency']),
'link' => $data['link'],
'image_link' => $data['image_link'],
'brand' => $config['brand'],
'google_product_category' => $config['google_product_category'],
];
// Add optional fields if present
if (!empty($data['item_group_id'])) {
$product['item_group_id'] = $data['item_group_id'];
}
if (!empty($data['additional_image_link'])) {
$product['additional_image_link'] = $data['additional_image_link'];
}
return $product;
}
//------------------------------------------
// Helper Functions
//------------------------------------------
function getMetaDescription($product_code, $fallback_description = '') {
global $meta_descriptions;
// First check for exact match
if (isset($meta_descriptions[$product_code])) {
return $meta_descriptions[$product_code];
}
// Then check for pattern match (starts with)
foreach ($meta_descriptions as $pattern => $description) {
if (strpos($product_code, $pattern) === 0) {
return $description;
}
}
// Return fallback if no match
return $fallback_description;
}
function calculateVariantPrice($base_price, $modifier_price, $modifier_type) {
if ($modifier_price <= 0) {
return $base_price;
}
switch ($modifier_type) {
case 1: // Addition
return $base_price + $modifier_price;
case 2: // Multiplication
return $base_price * $modifier_price;
default:
return $base_price;
}
}
function buildImageUrl($path, $base_url) {
if (empty($path)) return '';
return rtrim($base_url, '/') . '/' . ltrim($path, '/');
}
function buildProductUrl($slug, $base_url) {
if (empty($slug)) return '';
return rtrim($base_url, '/') . '/product/' . $slug;
}
function formatPrice($price, $currency) {
return number_format($price, 2, '.', '') . ' ' . $currency;
}
function cleanText($text) {
$text = strip_tags($text);
$text = preg_replace('/\s+/', ' ', $text);
return trim($text);
}
function formatAttributeName($name) {
// Remove prefix like "bracelet_"
$name = preg_replace('/^bracelet_/', '', $name);
// Replace underscores with spaces
$name = str_replace('_', ' ', $name);
// Capitalize words
return ucwords($name);
}
//------------------------------------------
// Output Functions
//------------------------------------------
function outputMetaFeedCSV($meta_feed) {
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="meta_product_feed.csv"');
$output = fopen('php://output', 'w');
// Write headers
if (!empty($meta_feed)) {
fputcsv($output, array_keys($meta_feed[0]));
// Write data
foreach ($meta_feed as $row) {
fputcsv($output, $row);
}
}
fclose($output);
}
function outputMetaFeedXML($meta_feed) {
header('Content-Type: application/xml; charset=utf-8');
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:g="http://base.google.com/ns/1.0"></rss>');
$channel = $xml->addChild('channel');
$channel->addChild('title', 'Morval Product Feed');
$channel->addChild('link', $GLOBALS['meta_config']['base_url']);
$channel->addChild('description', 'Product feed for Meta catalog');
foreach ($meta_feed as $product) {
$item = $channel->addChild('item');
foreach ($product as $key => $value) {
$xml_key = 'g:' . $key;
$item->addChild($xml_key, htmlspecialchars($value));
}
}
echo $xml->asXML();
}
function outputMetaFeedJSON($meta_feed) {
//header('Content-Type: application/json; charset=utf-8');
echo json_encode($meta_feed, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
?>

View File

@@ -1,7 +1,7 @@
var port, textEncoder, writableStreamClosed, writer, historyIndex = -1;
const lineHistory = [];
maintenanceRun = 0;
handshakeComplete = false;
let maintenanceRun = 0;
let handshakeComplete = false;
// Function to log communication to API
async function logCommunication(data, direction) {
@@ -102,6 +102,7 @@ async function connectSerial() {
// Log connection failure details
await logCommunication(`Serial connection failed: ${error.message || 'Unknown error'}`, 'disconnected');
alert("Serial Connection Failed");
window.location.reload();
}
}
@@ -580,7 +581,7 @@ async function closePort(){
maintenanceRun = 0; // reset maintenanceRun
// Refresh the page
window.location.reload();
setTimeout(() => {window.location.reload();}, 500);
}
async function closePortCarTest(){
@@ -736,7 +737,7 @@ async function updateHistory(){
body: testdetails
});
const historyresult = await response.json;
const historyresult = await response.json();
setTimeout(maintenanceTest,5000);
@@ -805,5 +806,6 @@ async function updateHistory(){
catch(e)
{
alert(e.message || e);
window.location.reload();
}
}

View File

@@ -1,5 +1,10 @@
const serialResultsDiv = document.getElementById("serialResults");
let port, textEncoder, writableStreamClosed, writer;
let readableStreamClosed, reader, openPort = 0;
let x, serial, fw, hw, sw, commitCode;
let firmwarelocation, upgraded_version, hex_fw;
// Buffer for accumulating received data before logging
let receivedDataBuffer = '';
@@ -113,13 +118,15 @@ async function connectDevice() {
// Log connection failure details
await logCommunication(`Serial connection failed: ${error.message || 'Unknown error'}`, 'disconnected');
if (openPort = 1){
if (openPort === 1){
closePort();
console.log("Closing port");
alert("System is still trying to close the serial port. If this message continues to come up please refresh this page.");
window.location.reload();
}
else{
alert("Your browser does not support this functionality. Please use latest Chrome or Edge browser.");
window.location.reload();
}
}
}
@@ -161,6 +168,7 @@ async function listenToPort() {
async function appendToTerminal(newStuff) {
serialResultsDiv.innerHTML += newStuff;
receivedDataBuffer += newStuff;
// Log received data
await logCommunication(newStuff.trim(), 'received');