05/31 Fix fetch price issues

This commit is contained in:
2026-05-31 22:06:10 -04:00
parent 9bc8bb3238
commit 67ed91d94c
3 changed files with 62 additions and 13 deletions
+16 -3
View File
@@ -257,6 +257,19 @@ def refresh_prices():
@investments_bp.route('/api/price/<ticker>') @investments_bp.route('/api/price/<ticker>')
@login_required @login_required
def api_price(ticker): def api_price(ticker):
"""Live price lookup for a ticker — used in the add transaction form.""" """Live price lookup for a ticker — used in the add/edit investment form."""
price = fetch_price(ticker) ticker = ticker.upper().strip()
return jsonify({'ticker': ticker.upper(), 'price': price}) error = None
price = None
try:
price = fetch_price(ticker)
if price is None:
error = f'No data returned for {ticker}. Check the ticker format.'
except Exception as e:
error = str(e)
return jsonify({
'ticker': ticker,
'price': price,
'error': error,
})
+26 -6
View File
@@ -80,18 +80,38 @@
document.getElementById('checkTicker').addEventListener('click', function() { document.getElementById('checkTicker').addEventListener('click', function() {
const ticker = document.getElementById('tickerInput').value.trim().toUpperCase(); const ticker = document.getElementById('tickerInput').value.trim().toUpperCase();
const result = document.getElementById('tickerResult'); const result = document.getElementById('tickerResult');
const btn = document.getElementById('checkTicker');
if (!ticker) { result.textContent = ''; return; } if (!ticker) { result.textContent = ''; return; }
result.innerHTML = '<span class="text-muted">Fetching…</span>';
fetch('/investments/api/price/' + encodeURIComponent(ticker)) result.innerHTML = '<span class="text-muted"><i class="bi bi-hourglass-split me-1"></i>Fetching…</span>';
btn.disabled = true;
// AbortController for 15s timeout
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 15000);
fetch('/investments/api/price/' + encodeURIComponent(ticker), { signal: controller.signal })
.then(r => r.json()) .then(r => r.json())
.then(data => { .then(data => {
if (data.price) { clearTimeout(timer);
result.innerHTML = '<span class="text-success"><i class="bi bi-check-circle me-1"></i>' + ticker + ' → {{ current_user.currency_symbol }}' + data.price.toFixed(4) + '</span>'; btn.disabled = false;
if (data.price != null) {
result.innerHTML = '<span class="text-success"><i class="bi bi-check-circle me-1"></i>'
+ ticker + ' → {{ current_user.currency_symbol }}' + data.price.toFixed(4) + '</span>';
} else { } else {
result.innerHTML = '<span class="text-danger"><i class="bi bi-x-circle me-1"></i>Ticker not found or no data</span>'; const msg = data.error || 'Ticker not found or no data';
result.innerHTML = '<span class="text-danger"><i class="bi bi-x-circle me-1"></i>' + msg + '</span>';
} }
}) })
.catch(() => { result.innerHTML = '<span class="text-warning">Could not fetch price</span>'; }); .catch(err => {
clearTimeout(timer);
btn.disabled = false;
if (err.name === 'AbortError') {
result.innerHTML = '<span class="text-warning"><i class="bi bi-clock me-1"></i>Request timed out — Yahoo Finance may be slow. Try again.</span>';
} else {
result.innerHTML = '<span class="text-warning"><i class="bi bi-exclamation-triangle me-1"></i>Network error. Check server connectivity.</span>';
}
});
}); });
</script> </script>
{% endblock %} {% endblock %}
@@ -105,17 +105,33 @@ priceInput.addEventListener('input', updateTotal);
const fillBtn = document.getElementById('fillPrice'); const fillBtn = document.getElementById('fillPrice');
if (fillBtn) { if (fillBtn) {
fillBtn.addEventListener('click', function() { fillBtn.addEventListener('click', function() {
fillBtn.disabled = true;
fillBtn.textContent = '…'; fillBtn.textContent = '…';
fetch('/investments/api/price/{{ inv.ticker }}')
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 15000);
fetch('/investments/api/price/{{ inv.ticker }}', { signal: controller.signal })
.then(r => r.json()) .then(r => r.json())
.then(data => { .then(data => {
if (data.price) { clearTimeout(timer);
fillBtn.disabled = false;
fillBtn.textContent = '↓';
if (data.price != null) {
priceInput.value = data.price.toFixed(4); priceInput.value = data.price.toFixed(4);
updateTotal(); updateTotal();
} else {
fillBtn.title = data.error || 'No price available';
fillBtn.style.color = '#ef4444';
setTimeout(() => { fillBtn.style.color = ''; fillBtn.title = ''; }, 3000);
} }
fillBtn.textContent = '↓';
}) })
.catch(() => { fillBtn.textContent = '↓'; }); .catch(err => {
clearTimeout(timer);
fillBtn.disabled = false;
fillBtn.textContent = '↓';
fillBtn.title = err.name === 'AbortError' ? 'Timed out' : 'Network error';
});
}); });
} }
{% endif %} {% endif %}