scrape fix

This commit is contained in:
Oli Passey
2025-06-27 17:25:56 +01:00
parent ee0142121a
commit 5726183115
27 changed files with 2353 additions and 621 deletions

View File

@@ -123,7 +123,8 @@
<ul class="mb-0 mt-2">
<li>Make sure URLs point to the specific product page</li>
<li>Test URLs in your browser first to ensure they work</li>
<li>Some sites may block automated requests - we'll handle this gracefully</li>
<li>The system will automatically prioritize <strong>delivery prices</strong> over collection prices</li>
<li>For JJ Food Service and A to Z Catering, ensure you can see delivery pricing on the page</li>
<li>For best results, use direct product page URLs</li>
</ul>
</div>
@@ -154,13 +155,15 @@
<h6 class="fw-bold">JJ Food Service</h6>
<p class="small text-muted">
Navigate to the specific product page on JJ Food Service and copy the URL.
Make sure you're logged in for accurate pricing.
Make sure you're logged in for accurate pricing. The system will automatically
prioritize <strong>delivery prices</strong> over collection prices.
</p>
<h6 class="fw-bold">A to Z Catering</h6>
<p class="small text-muted">
Go to the product page on A to Z Catering and copy the URL.
URLs typically contain "/products/product/" followed by the product name.
The system will automatically capture <strong>delivery pricing</strong> when available.
</p>
</div>
<div class="col-md-6">
@@ -170,10 +173,11 @@
The URL should contain "/dp/" followed by the product identifier.
</p>
<h6 class="fw-bold text-muted">Note</h6>
<h6 class="fw-bold text-success">Delivery Pricing Priority</h6>
<p class="small text-muted">
We focus on UK catering supply websites that work well with automated price tracking.
This provides reliable price monitoring for your business needs.
For JJ Food Service and A to Z Catering, the system automatically prioritizes
delivery prices over collection prices. This ensures you're tracking the
most relevant pricing for delivered goods to your business.
</p>
</div>
</div>

190
templates/edit_product.html Normal file
View File

@@ -0,0 +1,190 @@
{% extends "base.html" %}
{% block title %}Edit Product - Price Tracker{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h2 class="mb-0">
<i class="fas fa-edit me-2 text-primary"></i>Edit Product: {{ product.name }}
</h2>
</div>
<div class="card-body">
<form method="POST">
{{ form.hidden_tag() }}
<div class="row">
<div class="col-md-8 mb-3">
{{ form.name.label(class="form-label fw-bold") }}
{{ form.name(class="form-control form-control-lg") }}
{% if form.name.errors %}
<div class="text-danger small mt-1">
{% for error in form.name.errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
</div>
<div class="col-md-4 mb-3">
{{ form.target_price.label(class="form-label fw-bold") }}
<div class="input-group">
<span class="input-group-text">£</span>
{{ form.target_price(class="form-control form-control-lg") }}
</div>
{% if form.target_price.errors %}
<div class="text-danger small mt-1">
{% for error in form.target_price.errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
<small class="text-muted">Optional: Alert when price drops below this</small>
</div>
</div>
<div class="mb-3">
{{ form.description.label(class="form-label fw-bold") }}
{{ form.description(class="form-control", rows="3") }}
{% if form.description.errors %}
<div class="text-danger small mt-1">
{% for error in form.description.errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
</div>
<hr class="my-4">
<h5 class="mb-3">
<i class="fas fa-link me-2 text-secondary"></i>Store URLs
</h5>
<p class="text-muted small mb-3">Add URLs from the stores you want to track. At least one URL is required.</p>
<div class="row">
<div class="col-md-6 mb-3">
{{ form.jjfoodservice_url.label(class="form-label fw-bold") }}
<div class="input-group">
<span class="input-group-text">
<i class="fas fa-store text-primary"></i>
</span>
{{ form.jjfoodservice_url(class="form-control", placeholder="https://www.jjfoodservice.com/...") }}
</div>
{% if form.jjfoodservice_url.errors %}
<div class="text-danger small mt-1">
{% for error in form.jjfoodservice_url.errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
</div>
<div class="col-md-6 mb-3">
{{ form.atoz_catering_url.label(class="form-label fw-bold") }}
<div class="input-group">
<span class="input-group-text">
<i class="fas fa-store text-success"></i>
</span>
{{ form.atoz_catering_url(class="form-control", placeholder="https://www.atoz-catering.co.uk/...") }}
</div>
{% if form.atoz_catering_url.errors %}
<div class="text-danger small mt-1">
{% for error in form.atoz_catering_url.errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
{{ form.amazon_uk_url.label(class="form-label fw-bold") }}
<div class="input-group">
<span class="input-group-text">
<i class="fab fa-amazon text-warning"></i>
</span>
{{ form.amazon_uk_url(class="form-control", placeholder="https://www.amazon.co.uk/...") }}
</div>
{% if form.amazon_uk_url.errors %}
<div class="text-danger small mt-1">
{% for error in form.amazon_uk_url.errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<hr class="my-4">
<div class="d-flex justify-content-between">
<div>
<button type="submit" class="btn btn-primary btn-lg me-3">
<i class="fas fa-save me-2"></i>Update Product
</button>
<a href="{{ url_for('product_detail', product_id=product.id) }}" class="btn btn-outline-secondary btn-lg">
<i class="fas fa-arrow-left me-2"></i>Cancel
</a>
</div>
<!-- Delete button -->
<div>
<button type="button" class="btn btn-outline-danger btn-lg" data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="fas fa-trash me-2"></i>Delete Product
</button>
</div>
</div>
</form>
<!-- Help section -->
<div class="mt-5">
<div class="card bg-light">
<div class="card-body">
<h6 class="card-title">
<i class="fas fa-info-circle me-2 text-info"></i>How to find product URLs
</h6>
<ul class="card-text small mb-0">
<li><strong>JJ Food Service:</strong> Search for your product and copy the URL from the product page</li>
<li><strong>A to Z Catering:</strong> Navigate to the specific product and copy the URL</li>
<li><strong>Amazon UK:</strong> Find the product and copy the URL (we'll extract the essential part)</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">
<i class="fas fa-exclamation-triangle me-2 text-warning"></i>Confirm Delete
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete <strong>"{{ product.name }}"</strong>?</p>
<div class="alert alert-warning">
<i class="fas fa-warning me-2"></i>
<strong>Warning:</strong> This action cannot be undone. All price history for this product will be permanently deleted.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form method="POST" action="{{ url_for('delete_product', product_id=product.id) }}" style="display: inline;">
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-2"></i>Delete Product
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -97,6 +97,16 @@
<i class="fas fa-sync-alt me-1"></i>Scrape Now
</button>
</div>
<div class="btn-group" role="group">
<a href="{{ url_for('edit_product', product_id=product.id) }}" class="btn btn-outline-secondary">
<i class="fas fa-edit me-1"></i>Edit
</a>
<button class="btn btn-outline-danger delete-product-btn"
data-product-id="{{ product.id }}"
data-product-name="{{ product.name }}">
<i class="fas fa-trash me-1"></i>Delete
</button>
</div>
</div>
</div>
@@ -181,4 +191,58 @@
</div>
</div>
{% endif %}
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">
<i class="fas fa-exclamation-triangle me-2 text-warning"></i>Confirm Delete
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete <strong id="deleteProductName"></strong>?</p>
<div class="alert alert-warning">
<i class="fas fa-warning me-2"></i>
<strong>Warning:</strong> This action cannot be undone. All price history for this product will be permanently deleted.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form id="deleteForm" method="POST" style="display: inline;">
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-2"></i>Delete Product
</button>
</form>
</div>
</div>
</div>
</div>
<script>
// Handle delete product buttons
document.addEventListener('DOMContentLoaded', function() {
const deleteButtons = document.querySelectorAll('.delete-product-btn');
const deleteModal = document.getElementById('deleteModal');
const deleteForm = document.getElementById('deleteForm');
const deleteProductName = document.getElementById('deleteProductName');
deleteButtons.forEach(button => {
button.addEventListener('click', function() {
const productId = this.getAttribute('data-product-id');
const productName = this.getAttribute('data-product-name');
// Update modal content
deleteProductName.textContent = productName;
deleteForm.action = `/delete_product/${productId}`;
// Show modal
const modal = new bootstrap.Modal(deleteModal);
modal.show();
});
});
});
</script>
{% endblock %}

View File

@@ -14,6 +14,16 @@
<button class="btn btn-success me-2" onclick="scrapeProduct({{ product.id }})">
<i class="fas fa-sync-alt me-1"></i>Scrape Now
</button>
<a href="{{ url_for('edit_product', product_id=product.id) }}" class="btn btn-outline-primary me-2">
<i class="fas fa-edit me-1"></i>Edit
</a>
<button class="btn btn-outline-danger me-2 delete-product-btn"
data-product-id="{{ product.id }}"
data-product-name="{{ product.name }}"
data-bs-toggle="modal"
data-bs-target="#deleteModal">
<i class="fas fa-trash me-1"></i>Delete
</button>
<a href="{{ url_for('index') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i>Back to Dashboard
</a>
@@ -222,6 +232,35 @@
{% endif %}
</div>
</div>
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">
<i class="fas fa-exclamation-triangle me-2 text-warning"></i>Confirm Delete
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete <strong>"{{ product.name }}"</strong>?</p>
<div class="alert alert-warning">
<i class="fas fa-warning me-2"></i>
<strong>Warning:</strong> This action cannot be undone. All price history for this product will be permanently deleted.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form method="POST" action="{{ url_for('delete_product', product_id=product.id) }}" style="display: inline;">
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-2"></i>Delete Product
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
@@ -231,4 +270,20 @@
Plotly.newPlot('priceChart', chartData.data, chartData.layout, {responsive: true});
</script>
{% endif %}
<script>
// Handle delete product button
document.addEventListener('DOMContentLoaded', function() {
const deleteButton = document.querySelector('.delete-product-btn');
const deleteModal = document.getElementById('deleteModal');
if (deleteButton) {
deleteButton.addEventListener('click', function() {
// Show modal
const modal = new bootstrap.Modal(deleteModal);
modal.show();
});
}
});
</script>
{% endblock %}