Handle lastSeen column and all the discontinued items. #35

Merged
krzysiej merged 1 commits from feature/fix-discontinued-counter into master 2026-01-10 08:56:57 +01:00
6 changed files with 28 additions and 11 deletions

View File

@@ -38,7 +38,7 @@ class Migrate extends Command
$this->createProductsTable(); $this->createProductsTable();
$this->createPricesTable(); $this->createPricesTable();
$this->createStocksTable(); $this->createStocksTable();
$this->updateProductsTableAddPrices(); $this->addColumns();
return Command::SUCCESS; return Command::SUCCESS;
} }
@@ -95,7 +95,7 @@ class Migrate extends Command
} }
} }
public function updateProductsTableAddPrices(): void public function addColumns(): void
{ {
if (!Capsule::schema()->hasColumn('products', 'priceCurrent')) { if (!Capsule::schema()->hasColumn('products', 'priceCurrent')) {
Capsule::schema()->table('products', function (Blueprint $table) { Capsule::schema()->table('products', function (Blueprint $table) {
@@ -120,5 +120,11 @@ class Migrate extends Command
$table->float('lowestProductPrice30Days')->default(0); $table->float('lowestProductPrice30Days')->default(0);
}); });
} }
if (!Capsule::schema()->hasColumn('products', 'lastSeen')) {
Capsule::schema()->table('products', function (Blueprint $table) {
$table->date('last_seen')->nullable();
});
}
} }
} }

View File

@@ -7,6 +7,7 @@ namespace Krzysiej\RyobiCrawler\Command;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Database\Capsule\Manager as Capsule; use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Support\Facades\Date;
use Krzysiej\RyobiCrawler\Models\Price; use Krzysiej\RyobiCrawler\Models\Price;
use Krzysiej\RyobiCrawler\Models\Product; use Krzysiej\RyobiCrawler\Models\Product;
use Krzysiej\RyobiCrawler\Models\Stock; use Krzysiej\RyobiCrawler\Models\Stock;
@@ -51,10 +52,12 @@ class ScrapeWebsite extends Command
$progress->setMaxSteps(count($products)); $progress->setMaxSteps(count($products));
$progress->start(); $progress->start();
foreach($products as $product) { foreach($products as $product) {
$product->priceCurrent = $product->newestPrice->price; $newestPrice = $product->newestPrice;
$product->productStandardPrice = $product->newestPrice->productStandardPrice; $product->priceCurrent = $newestPrice->price;
$product->lowestProductPrice30Days = $product->newestPrice->lowestProductPrice30Days; $product->productStandardPrice = $newestPrice->productStandardPrice;
$product->lowestProductPrice30Days = $newestPrice->lowestProductPrice30Days;
$product->priceLowest = $product->lowestPrice->price; $product->priceLowest = $product->lowestPrice->price;
$product->lastSeen = $newestPrice->created_at->format('Y-m-d');
$product->save(['timestamps' => false]); $product->save(['timestamps' => false]);
$progress->advance(); $progress->advance();
} }
@@ -106,6 +109,7 @@ class ScrapeWebsite extends Command
$productModel->variantCode = $product->variantCode; $productModel->variantCode = $product->variantCode;
$productModel->modelCode = $product->modelCode; $productModel->modelCode = $product->modelCode;
$productModel->url = $product->url; $productModel->url = $product->url;
$productModel->lastSeen = date("Y-m-d");
$productModel->touch('updated_at'); $productModel->touch('updated_at');
$productModel->save(); $productModel->save();
$priceExists = $productModel->price()->whereRaw("strftime('%Y-%m-%d', created_at) = ?", [date('Y-m-d')])->exists(); $priceExists = $productModel->price()->whereRaw("strftime('%Y-%m-%d', created_at) = ?", [date('Y-m-d')])->exists();

View File

@@ -17,10 +17,9 @@ final class DiscontinuedController extends BaseController
return $this->render('productList.html.twig', ['listType' => 'discontinued']); return $this->render('productList.html.twig', ['listType' => 'discontinued']);
} }
$products = Product::where('updated_at', '<', now()->format('Y-m-d')) $products = Product::where('lastSeen', '<', now()->format('Y-m-d'))
->orderByDesc('starred') ->orderByDesc('starred')
->orderByDesc('created_by') ->orderByDesc('created_by')
->with(['currentPrice', 'lowestPrice'])
->get(); ->get();
return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'discontinued']); return $this->render('productList.html.twig', ['products' => $products, 'listType' => 'discontinued']);
} }

View File

@@ -2,6 +2,7 @@
namespace Krzysiej\RyobiCrawler\Models; namespace Krzysiej\RyobiCrawler\Models;
use Carbon\Traits\Date;
use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -24,6 +25,7 @@ use function Symfony\Component\Clock\now;
* @property float $priceLowest * @property float $priceLowest
* @property float $productStandardPrice * @property float $productStandardPrice
* @property float $lowestProductPrice30Days * @property float $lowestProductPrice30Days
* @property Date $lastSeen
*/ */
class Product extends Model class Product extends Model
{ {
@@ -81,7 +83,7 @@ class Product extends Model
public function isDiscontinued(): bool public function isDiscontinued(): bool
{ {
return $this->updated_at->format('Y-m-d') < now()->format('Y-m-d'); return $this->lastSeen < now()->format('Y-m-d');
} }
public function isNew(): bool public function isNew(): bool
{ {

View File

@@ -49,7 +49,7 @@ class AppExtension extends AbstractExtension
public function discontinuedCount(): int public function discontinuedCount(): int
{ {
return Product::where('updated_at', '<', now()->format('Y-m-d'))->count(); return Product::where('lastSeen', '<>', now()->format('Y-m-d'))->count();
} }
public function findByCreatedAtDate(Collection $items, string $date): Stock|Price|null public function findByCreatedAtDate(Collection $items, string $date): Stock|Price|null

View File

@@ -29,7 +29,7 @@
<span class="badge text-bg-warning">out of stock</span> <span class="badge text-bg-warning">out of stock</span>
{% endif %} {% endif %}
{% if product.isDiscontinued() %} {% if product.isDiscontinued() %}
<span class="badge text-bg-secondary" data-bs-toggle="tooltip" data-bs-title="Last update: {{ product.updated_at }}">is discontinued</span> <span class="badge text-bg-secondary" data-bs-toggle="tooltip" data-bs-title="Last update: {{ product.lastSeen }}">is discontinued</span>
{% endif %} {% endif %}
{% if product.isNew() %} {% if product.isNew() %}
<span class="badge text-bg-success">is new</span> <span class="badge text-bg-success">is new</span>
@@ -46,7 +46,13 @@
</nav> </nav>
</td> </td>
<td class="align-middle"><a href='https://pl.ryobitools.eu/{{ product.url }}'>link</a></td> <td class="align-middle"><a href='https://pl.ryobitools.eu/{{ product.url }}'>link</a></td>
<td class="align-middle text-end">{% if product.priceLowest != product.priceCurrent %}{{ product.priceLowest | format_currency('PLN', {}, 'pl') }}{%else%}<span class="badge text-bg-info">now lowest</span>{% endif %}</td> <td class="align-middle text-end">
{% if product.isDiscontinued() %}
{{ product.priceLowest | format_currency('PLN', {}, 'pl') }}
{% else %}
{% if product.priceLowest != product.priceCurrent %}{{ product.priceLowest | format_currency('PLN', {}, 'pl') }}{%else%}<span class="badge text-bg-info">now lowest</span>{% endif %}</td>
{% endif %}
<td class="align-middle text-end">{{ product.priceCurrent | format_currency('PLN', {}, 'pl') }}</td> <td class="align-middle text-end">{{ product.priceCurrent | format_currency('PLN', {}, 'pl') }}</td>
<td class="align-middle"> <td class="align-middle">
<div class="d-flex flex-row"> <div class="d-flex flex-row">