Files
EverShelf/api/cron_smart_shopping.php
T
dadaloop82 f65fb4365c fix: shopping list accuracy, Bring! cleanup server-side, vacuum prompt, recipe appliances
## v1.7.5

### Added
- Vacuum sealed prompt after item use (conf/weighted units, auto-dismiss 8s)
- Multi-function appliance awareness in Gemini recipe prompts (Cookeo/Bimby/Thermomix)
- Server-side Bring! cleanup in cron (no client page load required)
- shopping_name field in inventory_list API response

### Fixed
- Bring! cleanup: false token match (Succo/Frutta from product name tokens)
- Bring! cleanup: expired item with fresh family stock no longer flagged critical (Verdure)
- Bring! remove: catalog items now removed via German key fallback (Formaggio→Käse)
- Shopping list: isExpiringSoon false positives (requires pctLeft < 50%)
- Shopping list: expired batch suppressed when fresh restock >= 50%
- Cross-device cleanup: detect app-added items via spec markers not localStorage
- API fetch: cache: 'no-store' on all api() calls
- Shopping page: 45s polling for multi-client sync
2026-05-10 10:54:35 +00:00

76 lines
3.0 KiB
PHP

<?php
/**
* Cron: pre-compute smart shopping list and save to cache.
* Install with: crontab -e
* *\/5 * * * * php /var/www/html/evershelf/api/cron_smart_shopping.php >> /var/www/html/evershelf/data/cron.log 2>&1
*/
// Only allow CLI execution — block HTTP access
if (PHP_SAPI !== 'cli') {
http_response_code(403);
exit('Forbidden');
}
// Define CRON_MODE before loading index.php so the router is skipped
define('CRON_MODE', true);
// Load all API functions without running the HTTP router
require_once __DIR__ . '/index.php';
const CACHE_FILE = __DIR__ . '/../data/smart_shopping_cache.json';
try {
$db = getDB();
// Capture the JSON output of smartShopping()
ob_start();
smartShopping($db);
$json = ob_get_clean();
$decoded = json_decode($json, true);
if (!$decoded || !isset($decoded['success'])) {
throw new RuntimeException('Invalid JSON from smartShopping(): ' . substr($json, 0, 200));
}
$decoded['cached_at'] = date('c');
$decoded['cached_ts'] = time();
if (file_put_contents(CACHE_FILE, json_encode($decoded, JSON_UNESCAPED_UNICODE)) === false) {
throw new RuntimeException('Cannot write cache file: ' . CACHE_FILE);
}
$itemCount = count($decoded['items'] ?? []);
echo '[' . date('Y-m-d H:i:s') . '] OK — ' . $itemCount . " items cached\n";
// ── Bring! server-side cleanup ────────────────────────────────────────
// After computing smart shopping, automatically remove stale Bring! items
// and add/update critical ones. This runs fully server-side every cron cycle.
try {
$cleanupResult = bringCleanupObsolete($db);
if (isset($cleanupResult['skipped'])) {
echo '[' . date('Y-m-d H:i:s') . '] Bring! cleanup skipped: ' . $cleanupResult['skipped'] . "\n";
} else {
echo '[' . date('Y-m-d H:i:s') . '] Bring! cleanup — removed: ' . ($cleanupResult['removed'] ?? 0)
. '/' . ($cleanupResult['candidates'] ?? 0) . ' candidates'
. ($cleanupResult['errors'] ? ', errors: ' . $cleanupResult['errors'] : '') . "\n";
}
$addResult = bringAutoAddCritical($db);
if (isset($addResult['skipped'])) {
echo '[' . date('Y-m-d H:i:s') . '] Bring! auto-add skipped: ' . $addResult['skipped'] . "\n";
} else {
echo '[' . date('Y-m-d H:i:s') . '] Bring! auto-add — added: ' . ($addResult['added'] ?? 0)
. ', updated specs: ' . ($addResult['updated'] ?? 0) . "\n";
}
} catch (Throwable $be) {
echo '[' . date('Y-m-d H:i:s') . '] Bring! sync warning: ' . $be->getMessage() . "\n";
}
} catch (Throwable $e) {
$msg = $e->getMessage();
echo '[' . date('Y-m-d H:i:s') . '] ERROR: ' . $msg . "\n";
// Report to GitHub Issues (uses the same _phpErrorReport from index.php)
_phpErrorReport($msg, $e->getFile(), $e->getLine(), $e->getTraceAsString(), get_class($e));
exit(1);
}