Track Filament

This commit is contained in:
Hickmeister
2025-01-05 02:28:51 +00:00
parent e31bd43eaa
commit 5a11305caf
7 changed files with 1664 additions and 2 deletions
+131
View File
@@ -0,0 +1,131 @@
<?php
require '../../vendor/autoload.php';
require '../db.php';
require_once '../envLoader.php';
loadEnv(__DIR__ . '/../../.env');
// Check if the user is logged in
if (!isset($_SESSION['userId'])) {
echo json_encode(['status' => 'error', 'message' => 'User not authenticated.']);
exit;
}
use Goutte\Client;
// Start session to get user ID
session_start();
if (!isset($_SESSION['userId'])) {
echo json_encode(['status' => 'error', 'message' => 'User not authenticated.']);
exit;
}
// Check for POST data
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$filamentName = $_POST['productName'] ?? '';
$amazonUrl = $_POST['productUrl'] ?? '';
$userId = $_SESSION['userId'];
// Basic validation
if (empty($filamentName) || empty($amazonUrl)) {
echo json_encode(['status' => 'error', 'message' => 'Filament name and URL are required.']);
exit;
}
$client = new Client();
try {
// Scrape the Amazon page for initial price and other details
$crawler = $client->request('GET', $amazonUrl);
// Scrape Price
$whole = $crawler->filter('.a-price-whole')->count() ? $crawler->filter('.a-price-whole')->text() : '0';
$fraction = $crawler->filter('.a-price-fraction')->count() ? $crawler->filter('.a-price-fraction')->text() : '00';
$whole = preg_replace('/[^0-9]/', '', $whole);
$fraction = preg_replace('/[^0-9]/', '', $fraction);
$fraction = strlen($fraction) === 1 ? $fraction . '0' : substr($fraction, 0, 2);
$totalPrice = floatval($whole . '.' . $fraction);
// Scrape Filament Details from Table
$details = [];
$crawler->filter('table.a-normal tr')->each(function ($node) use (&$details) {
$label = trim($node->filter('td.a-span3')->text());
$value = trim($node->filter('td.a-span9')->text());
$details[$label] = $value;
});
// Extract details or use default if not found
$brand = $details['Brand'] ?? 'Unknown';
$material = $details['Material'] ?? 'Unknown';
$color = $details['Colour'] ?? 'Unknown';
$filamentWeight = isset($details['Item weight']) ? preg_replace('/[^0-9.]/', '', $details['Item weight']) : 1;
if (stripos($details['Item weight'], 'kilograms') !== false || stripos($details['Item weight'], 'kg') !== false) {
$filamentWeight = $filamentWeight * 1000; // Convert KG to G
}
$itemDiameter = isset($details['Item diameter']) ? preg_replace('/[^0-9.]/', '', $details['Item diameter']) : 1.75;
// Start a transaction to ensure consistency
$pdo->beginTransaction();
// Check if filament already exists
$stmt = $pdo->prepare("SELECT id FROM filamentTracker WHERE amazonUrl = :amazonUrl");
$stmt->execute([':amazonUrl' => $amazonUrl]);
$existingFilament = $stmt->fetch(PDO::FETCH_ASSOC);
if ($existingFilament) {
// Filament exists – just update the price history
$filamentId = $existingFilament['id'];
$stmt = $pdo->prepare("
INSERT INTO filamentPriceHistory (filamentId, price)
VALUES (:filamentId, :price)
");
$stmt->execute([
':filamentId' => $filamentId,
':price' => $totalPrice
]);
$message = 'Filament price updated successfully.';
} else {
// Insert new filament into filamentTracker
$stmt = $pdo->prepare("
INSERT INTO filamentTracker (userId, filamentName, amazonUrl, filamentWeight, brand, material, color, itemDiameter)
VALUES (:userId, :filamentName, :amazonUrl, :filamentWeight, :brand, :material, :color, :itemDiameter)
");
$stmt->execute([
':userId' => $userId,
':filamentName' => $filamentName,
':amazonUrl' => $amazonUrl,
':filamentWeight' => $filamentWeight,
':brand' => $brand,
':material' => $material,
':color' => $color,
':itemDiameter' => $itemDiameter
]);
// Get the last inserted filament ID
$filamentId = $pdo->lastInsertId();
// Insert initial price into filamentPriceHistory
$stmt = $pdo->prepare("
INSERT INTO filamentPriceHistory (filamentId, price)
VALUES (:filamentId, :price)
");
$stmt->execute([
':filamentId' => $filamentId,
':price' => $totalPrice
]);
$message = $filamentName . ' Filament added successfully and price tracked.';
}
// Commit the transaction
$pdo->commit();
echo json_encode(['status' => 'success', 'message' => $message]);
} catch (Exception $e) {
$pdo->rollBack();
echo json_encode(['status' => 'error', 'message' => 'Failed to scrape or insert data. Error: ' . $e->getMessage()]);
}
} else {
echo json_encode(['status' => 'error', 'message' => 'Invalid request method.']);
}
?>
+68
View File
@@ -0,0 +1,68 @@
<?php
require '../../vendor/autoload.php';
require '../db.php';
require_once '../envLoader.php';
loadEnv(__DIR__ . '/../../.env');
// Check if the user is logged in
if (!isset($_SESSION['userId'])) {
echo json_encode(['status' => 'error', 'message' => 'User not authenticated.']);
exit;
}
header('Content-Type: application/json');
// Fetch all filament prices from the database
try {
$stmt = $pdo->query("
SELECT
ft.filamentName,
ft.brand,
ft.material,
ft.color,
fp.price,
fp.recordedAt
FROM
filamentTracker ft
JOIN
filamentPriceHistory fp ON ft.id = fp.filamentId
ORDER BY
ft.filamentName,
fp.recordedAt ASC
");
$filaments = $stmt->fetchAll(PDO::FETCH_ASSOC);
$result = [];
// Format data for charts (grouped by filament)
foreach ($filaments as $filament) {
$name = $filament['filamentName'];
if (!isset($result[$name])) {
$result[$name] = [
'brand' => $filament['brand'],
'material' => $filament['material'],
'color' => $filament['color'],
'prices' => []
];
}
$result[$name]['prices'][] = [
'price' => $filament['price'],
'recordedAt' => $filament['recordedAt']
];
}
// Return as JSON
echo json_encode([
'status' => 'success',
'data' => $result
], JSON_PRETTY_PRINT);
} catch (PDOException $e) {
echo json_encode([
'status' => 'error',
'message' => 'Failed to fetch filament prices: ' . $e->getMessage()
]);
}
?>
@@ -0,0 +1,98 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require '../../vendor/autoload.php';
require '../db.php';
require_once '../envLoader.php';
loadEnv(__DIR__ . '/../../.env');
use Goutte\Client;
function scrapeFilamentPrice($productName, $productUrl) {
$client = new Client();
$crawler = $client->request('GET', $productUrl);
// Extract the current price
$price = $crawler->filter('span.a-price-whole')->first()->text();
$price = str_replace(',', '', $price); // Clean the price string
// Extract the original price (if available)
$originalPriceNode = $crawler->filter('span.a-price.a-text-price')->first();
$originalPrice = $originalPriceNode->count() ? str_replace(',', '', $originalPriceNode->text()) : null;
// Calculate the discount percentage
$discountPercentage = null;
if ($originalPrice) {
$discountPercentage = round(100 - (($price / $originalPrice) * 100), 2);
}
$currency = 'GBP';
return [
'productName' => $productName,
'productUrl' => $productUrl,
'price' => $price,
'originalPrice' => $originalPrice,
'discountPercentage' => $discountPercentage,
'currency' => $currency
];
}
// Handle Form Submission (Add New Filament to Track)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$productName = $_POST['productName'];
$productUrl = $_POST['productUrl'];
$result = scrapeFilamentPrice($productName, $productUrl);
try {
$stmt = $pdo->prepare("
INSERT INTO filamentPrices (productName, productUrl, price, originalPrice, discountPercentage, currency)
VALUES (:productName, :productUrl, :price, :originalPrice, :discountPercentage, :currency)
");
$stmt->execute([
':productName' => $result['productName'],
':productUrl' => $result['productUrl'],
':price' => $result['price'],
':originalPrice' => $result['originalPrice'],
':discountPercentage' => $result['discountPercentage'],
':currency' => $result['currency']
]);
echo json_encode(['status' => 'success', 'message' => 'Filament price recorded successfully!']);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => 'Failed to record price: ' . $e->getMessage()]);
}
}
// Handle Scheduled Scraping for Existing URLs (Cron Job)
if (isset($argv[1]) && $argv[1] === 'cron') {
try {
$stmt = $pdo->query("SELECT * FROM filamentPrices GROUP BY productUrl");
$filaments = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($filaments as $filament) {
$result = scrapeFilamentPrice($filament['productName'], $filament['productUrl']);
$stmt = $pdo->prepare("
INSERT INTO filamentPrices (productName, productUrl, price, originalPrice, discountPercentage, currency)
VALUES (:productName, :productUrl, :price, :originalPrice, :discountPercentage, :currency)
");
$stmt->execute([
':productName' => $result['productName'],
':productUrl' => $result['productUrl'],
':price' => $result['price'],
':originalPrice' => $result['originalPrice'],
':discountPercentage' => $result['discountPercentage'],
':currency' => $result['currency']
]);
echo "Recorded: {$result['productName']} - £{$result['price']} (Discount: {$result['discountPercentage']}%)\n";
}
} catch (PDOException $e) {
echo "Failed to fetch filament data: " . $e->getMessage();
}
}
?>
@@ -0,0 +1,63 @@
<?php
require '/mnt/www-live/TechOdyssey_Designs_Dashboard/vendor/autoload.php';
require '/mnt/www-live/TechOdyssey_Designs_Dashboard/src/db.php';
require_once '/mnt/www-live/TechOdyssey_Designs_Dashboard/src/envLoader.php';
loadEnv(__DIR__ . '/../../.env');
use Goutte\Client;
// Function to scrape price
function scrapePrice($url) {
$client = new Client(); // Reinitialize client for each request
try {
$crawler = $client->request('GET', $url);
$whole = $crawler->filter('.a-price-whole')->count() ? $crawler->filter('.a-price-whole')->text() : '0';
$fraction = $crawler->filter('.a-price-fraction')->count() ? $crawler->filter('.a-price-fraction')->text() : '00';
$whole = preg_replace('/[^0-9]/', '', $whole);
$fraction = preg_replace('/[^0-9]/', '', $fraction);
$fraction = strlen($fraction) === 1 ? $fraction . '0' : substr($fraction, 0, 2);
$totalPrice = floatval($whole . '.' . $fraction);
return $totalPrice;
} catch (Exception $e) {
return null;
}
}
// Fetch all filaments
try {
$stmt = $pdo->query("SELECT * FROM filamentTracker");
$filaments = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($filaments as $filament) {
$amazonUrl = $filament['amazonUrl'];
$filamentId = $filament['id'];
// Scrape price
$totalPrice = scrapePrice($amazonUrl);
if ($totalPrice !== null && $totalPrice > 0) {
$stmt = $pdo->prepare("
INSERT INTO filamentPriceHistory (filamentId, price)
VALUES (:filamentId, :price)
");
$stmt->execute([
':filamentId' => $filamentId,
':price' => $totalPrice
]);
echo "Updated price for {$filament['filamentName']}: £{$totalPrice}\n";
} else {
echo "Failed to update {$filament['filamentName']} (no price found or £0).\n";
}
// Add a small random delay between 1 to 5 seconds
sleep(rand(1, 5));
}
} catch (PDOException $e) {
echo "Database error: " . $e->getMessage();
}
?>