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':
|
||||
bringCleanSpecs();
|
||||
break;
|
||||
case 'bring_migrate_names':
|
||||
bringMigrateNames($db);
|
||||
break;
|
||||
case 'bring_suggest':
|
||||
bringSuggestItems($db);
|
||||
break;
|
||||
@@ -4197,13 +4200,86 @@ function bringCleanSpecs(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve smart shopping from cache (written by cron), falling back to live computation.
|
||||
* Cache is valid for up to 10 minutes; if stale or missing, compute on the fly.
|
||||
*/
|
||||
/**
|
||||
* Invalidate the smart shopping cache so the next request recomputes live.
|
||||
* Call after any inventory_add or inventory_use that changes stock meaningfully.
|
||||
* Migrate existing Bring! list items to use generic shopping names.
|
||||
* 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.
|
||||
*/
|
||||
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 {
|
||||
$cacheFile = __DIR__ . '/../data/smart_shopping_cache.json';
|
||||
if (file_exists($cacheFile)) {
|
||||
|
||||
@@ -7416,6 +7416,30 @@ function renderSmartItem(item) {
|
||||
</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() {
|
||||
const checks = document.querySelectorAll('.smart-check:checked');
|
||||
if (checks.length === 0) {
|
||||
|
||||
@@ -735,6 +735,12 @@
|
||||
<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>
|
||||
</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>
|
||||
<!-- Recipe Tab -->
|
||||
|
||||
Reference in New Issue
Block a user