feat: migrate existing Bring! items to generic shopping names
- New API action bring_migrate_names: reads current Bring! list, matches items against products DB, replaces specific names with shopping_name (e.g. 'Mortadella IGP' → 'Affettato' with spec 'Mortadella IGP · Brand') - New button in Bring! settings: 'Generalizza nomi lista Bring!' with live status feedback (migrated / skipped / errors count) - Auto-refreshes shopping list view after migration
This commit is contained in:
+82
-6
@@ -254,6 +254,9 @@ try {
|
|||||||
case 'bring_clean_specs':
|
case 'bring_clean_specs':
|
||||||
bringCleanSpecs();
|
bringCleanSpecs();
|
||||||
break;
|
break;
|
||||||
|
case 'bring_migrate_names':
|
||||||
|
bringMigrateNames($db);
|
||||||
|
break;
|
||||||
case 'bring_suggest':
|
case 'bring_suggest':
|
||||||
bringSuggestItems($db);
|
bringSuggestItems($db);
|
||||||
break;
|
break;
|
||||||
@@ -4197,13 +4200,86 @@ function bringCleanSpecs(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serve smart shopping from cache (written by cron), falling back to live computation.
|
* Migrate existing Bring! list items to use generic shopping names.
|
||||||
* Cache is valid for up to 10 minutes; if stale or missing, compute on the fly.
|
* For each item on the list that matches a product in the DB, if the item name
|
||||||
*/
|
* is the specific product name (not the generic shopping_name), replace it with
|
||||||
/**
|
* the generic name and move the specific name to the specification field.
|
||||||
* Invalidate the smart shopping cache so the next request recomputes live.
|
|
||||||
* Call after any inventory_add or inventory_use that changes stock meaningfully.
|
|
||||||
*/
|
*/
|
||||||
|
function bringMigrateNames(PDO $db): void {
|
||||||
|
$auth = bringAuth();
|
||||||
|
if (!$auth) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Credenziali Bring! non configurate']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$listUUID = $auth['bringListUUID'];
|
||||||
|
if (empty($listUUID)) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Lista non trovata']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}");
|
||||||
|
if (!$data || !isset($data['purchase'])) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Errore nel recupero della lista']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a lookup: product name (lowercase) → [shopping_name, brand]
|
||||||
|
$products = $db->query("SELECT name, brand, shopping_name FROM products WHERE shopping_name IS NOT NULL AND shopping_name != ''")->fetchAll();
|
||||||
|
$lookup = [];
|
||||||
|
foreach ($products as $p) {
|
||||||
|
$lookup[mb_strtolower($p['name'])] = ['shopping_name' => $p['shopping_name'], 'brand' => $p['brand'] ?? ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
$migrated = 0;
|
||||||
|
$skipped = 0;
|
||||||
|
$errors = 0;
|
||||||
|
|
||||||
|
foreach ($data['purchase'] as $item) {
|
||||||
|
$rawName = $item['name'] ?? '';
|
||||||
|
$itName = bringToItalian($rawName); // translate from Bring internal name if needed
|
||||||
|
$key = mb_strtolower($itName);
|
||||||
|
$spec = $item['specification'] ?? '';
|
||||||
|
|
||||||
|
if (!isset($lookup[$key])) { $skipped++; continue; }
|
||||||
|
|
||||||
|
$shoppingName = $lookup[$key]['shopping_name'];
|
||||||
|
$brand = $lookup[$key]['brand'];
|
||||||
|
|
||||||
|
// Already using the generic name → nothing to do
|
||||||
|
if (mb_strtolower($rawName) === mb_strtolower($shoppingName)) { $skipped++; continue; }
|
||||||
|
if (mb_strtolower(bringToItalian($rawName)) === mb_strtolower($shoppingName)) { $skipped++; continue; }
|
||||||
|
|
||||||
|
// Build new spec: "Specific Name · Brand" (keep existing spec if already set & different)
|
||||||
|
$newSpec = $itName . ($brand ? " · {$brand}" : '');
|
||||||
|
if ($spec !== '' && $spec !== $newSpec && stripos($spec, $itName) === false) {
|
||||||
|
// There's a custom spec the user wrote — prepend the product name
|
||||||
|
$newSpec = $itName . ($brand ? " · {$brand}" : '') . ' — ' . $spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: remove old item
|
||||||
|
$removeBody = http_build_query([
|
||||||
|
'uuid' => $listUUID,
|
||||||
|
'purchase' => $rawName,
|
||||||
|
]);
|
||||||
|
bringRequest('DELETE', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}/{$rawName}");
|
||||||
|
|
||||||
|
// Step 2: add with generic name + spec
|
||||||
|
$addBody = http_build_query([
|
||||||
|
'uuid' => $listUUID,
|
||||||
|
'purchase' => $shoppingName,
|
||||||
|
'specification' => $newSpec,
|
||||||
|
]);
|
||||||
|
$result = bringRequest('PUT', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}", $addBody);
|
||||||
|
if ($result !== false) {
|
||||||
|
$migrated++;
|
||||||
|
} else {
|
||||||
|
$errors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'migrated' => $migrated, 'skipped' => $skipped, 'errors' => $errors]);
|
||||||
|
}
|
||||||
|
|
||||||
function invalidateSmartShoppingCache(): void {
|
function invalidateSmartShoppingCache(): void {
|
||||||
$cacheFile = __DIR__ . '/../data/smart_shopping_cache.json';
|
$cacheFile = __DIR__ . '/../data/smart_shopping_cache.json';
|
||||||
if (file_exists($cacheFile)) {
|
if (file_exists($cacheFile)) {
|
||||||
|
|||||||
@@ -7416,6 +7416,30 @@ function renderSmartItem(item) {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function migrateBringNames(btn) {
|
||||||
|
const statusEl = document.getElementById('bring-migrate-status');
|
||||||
|
if (btn) btn.disabled = true;
|
||||||
|
if (statusEl) { statusEl.style.display = 'inline'; statusEl.textContent = '⏳ In corso…'; }
|
||||||
|
try {
|
||||||
|
const data = await api('bring_migrate_names', {}, 'POST', {});
|
||||||
|
if (data.success) {
|
||||||
|
const msg = `✅ ${data.migrated} aggiornati, ${data.skipped} già ok${data.errors ? `, ${data.errors} errori` : ''}`;
|
||||||
|
if (statusEl) statusEl.textContent = msg;
|
||||||
|
if (data.migrated > 0) {
|
||||||
|
showToast(`🔄 ${data.migrated} nomi generalizzati in Bring!`, 'success');
|
||||||
|
loadShoppingList(); // refresh the shopping list view
|
||||||
|
} else {
|
||||||
|
showToast('Tutti i nomi sono già aggiornati', 'info');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (statusEl) statusEl.textContent = '❌ ' + (data.error || 'Errore');
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
if (statusEl) statusEl.textContent = '❌ Errore di connessione';
|
||||||
|
}
|
||||||
|
if (btn) btn.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
async function addSmartToBring() {
|
async function addSmartToBring() {
|
||||||
const checks = document.querySelectorAll('.smart-check:checked');
|
const checks = document.querySelectorAll('.smart-check:checked');
|
||||||
if (checks.length === 0) {
|
if (checks.length === 0) {
|
||||||
|
|||||||
@@ -735,6 +735,12 @@
|
|||||||
<input type="password" id="setting-bring-password" class="form-input" placeholder="Password">
|
<input type="password" id="setting-bring-password" class="form-input" placeholder="Password">
|
||||||
<button class="btn btn-small btn-secondary mt-2" onclick="togglePasswordVisibility('setting-bring-password')">👁️ Mostra/Nascondi</button>
|
<button class="btn btn-small btn-secondary mt-2" onclick="togglePasswordVisibility('setting-bring-password')">👁️ Mostra/Nascondi</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" style="margin-top:16px;padding-top:14px;border-top:1px solid var(--border)">
|
||||||
|
<label>🔄 Aggiorna nomi nella lista</label>
|
||||||
|
<p style="font-size:0.82rem;color:var(--text-muted);margin:4px 0 10px">Sostituisce i nomi specifici con quelli generici (es. "Mortadella IGP" → "Affettato") negli item già presenti in Bring!.</p>
|
||||||
|
<button class="btn btn-secondary btn-small" onclick="migrateBringNames(this)">🔄 Generalizza nomi lista Bring!</button>
|
||||||
|
<span id="bring-migrate-status" style="display:none;margin-left:8px;font-size:0.85rem"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Recipe Tab -->
|
<!-- Recipe Tab -->
|
||||||
|
|||||||
Reference in New Issue
Block a user