From 6bdf83a971a4aaf9ff19b9a65653d7d5234e692e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20=C5=A0trauch?= Date: Mon, 15 Sep 2025 13:52:37 +0200 Subject: [PATCH] More days, safer caching --- calculator/index.html | 233 +++++++++++++++++++++++++++--------------- calculator/miner.py | 10 +- 2 files changed, 157 insertions(+), 86 deletions(-) diff --git a/calculator/index.html b/calculator/index.html index 3360bb8..a5be37e 100644 --- a/calculator/index.html +++ b/calculator/index.html @@ -92,91 +92,160 @@ // Reload data every hour (3600000 milliseconds) setInterval(loadAllData, 3600000); }; + async function createTableRows() { + const tbody = document.getElementById('prices-table-body'); + tbody.innerHTML = ''; + const today = new Date(); + const tomorrow = new Date(today); + tomorrow.setDate(today.getDate() + 1); + const tomorrowString = formatDate(tomorrow); + // Check if tomorrow's data is available + let tomorrowAvailable = false; + try { + const resp = await fetch('/price/day/' + tomorrowString + '?num_cheapest_hours=8'); + const data = await resp.json(); + if (!data.detail || data.detail !== "prices not found") { + tomorrowAvailable = true; + } + } catch (e) {} + + let rowCount = tomorrowAvailable ? 15 : 14; + for (let offset = 0; offset < rowCount; offset++) { + let date; + let label; + if (tomorrowAvailable && offset === 0) { + date = tomorrow; + label = `Zítra (${tomorrowString})`; + } else { + date = new Date(today); + date.setDate(today.getDate() - (tomorrowAvailable ? offset - 1 : offset)); + const dateString = formatDate(date); + label = offset === (tomorrowAvailable ? 1 : 0) ? `Dnes (${dateString})` : dateString; + } + const dateString = formatDate(date); + const rowId = `row-${dateString}`; + const tr = document.createElement('tr'); + tr.id = rowId; + // Date cell + const dateCell = document.createElement('td'); + dateCell.className = 'px-1 bg-neutral-200 w-16'; + dateCell.id = `date-${dateString}`; + dateCell.innerText = label; + tr.appendChild(dateCell); + // Hour cells + for (let i = 0; i < 24; i++) { + const td = document.createElement('td'); + td.className = 'px-1'; + td.id = `${dateString}-${i}`; + td.innerText = '-'; + tr.appendChild(td); + } + tbody.appendChild(tr); + } + } + + function loadData(date, isToday) { + fetch('/price/day/' + date + '?num_cheapest_hours=8') + .then(response => response.json()) + .then(data => { + if (data.detail == "prices not found") { + for (let i = 0; i < 24; i++) { + document.getElementById(`${date}-${i}`).innerText = "-"; + } + return; + } + for (let i = 0; i < 24; i++) { + let extra_class = ""; + let border = "border-dotted"; + if (isToday && i == new Date().getHours()) { + extra_class = " font-bold text-xl"; + border = "border-solid"; + } + let value = Math.round(data.total.hours[i] * 100) / 100; + let td = document.getElementById(`${date}-${i}`); + if (data.cheapest_hours_by_average.hours.includes(i)) { + td.className = `px-1 ${border} border-4 border-lime-400 ${getBgColor(value)}${extra_class}`; + } else if (data.most_expensive_hours_by_average.hours.includes(i)) { + td.className = `px-1 ${border} border-4 border-rose-400 ${getBgColor(value)}${extra_class}`; + } else { + if (extra_class != "") { + extra_class = " border-4 border-solid border-gray-500"; + } + td.className = `px-1 ${getBgColor(value)}${extra_class}`; + } + td.innerText = value; + } + }); + } + + async function loadAllData() { + await createTableRows(); + const today = new Date(); + const tomorrow = new Date(today); + tomorrow.setDate(today.getDate() + 1); + const tomorrowString = formatDate(tomorrow); + // Check if tomorrow's data is available + let tomorrowAvailable = false; + try { + const resp = await fetch('/price/day/' + tomorrowString + '?num_cheapest_hours=8'); + const data = await resp.json(); + if (!data.detail || data.detail !== "prices not found") { + tomorrowAvailable = true; + } + } catch (e) {} + let rowCount = tomorrowAvailable ? 15 : 14; + for (let offset = 0; offset < rowCount; offset++) { + let date; + if (tomorrowAvailable && offset === 0) { + date = tomorrow; + } else { + date = new Date(today); + date.setDate(today.getDate() - (tomorrowAvailable ? offset - 1 : offset)); + } + const dateString = formatDate(date); + loadData(dateString, (tomorrowAvailable ? offset === 1 : offset === 0)); + } + } + + window.onload = function() { + loadAllData(); + setInterval(loadAllData, 3600000); + }; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
000102030405060708091011121314151617181920212223
Dnes------------------------
Zítra------------------------
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Datum000102030405060708091011121314151617181920212223
diff --git a/calculator/miner.py b/calculator/miner.py index b3bd988..c2c656c 100644 --- a/calculator/miner.py +++ b/calculator/miner.py @@ -33,15 +33,17 @@ def get_energy_prices(d: datetime.date=datetime.date.today(), no_cache:bool=Fals if not hours or no_cache: r = requests.get(url_energy.format(date_str)) - try: for raw in r.json()["data"]["dataLine"][1]["point"]: hours[str(int(raw["x"])-1)] = raw["y"] except IndexError: raise PriceNotFound() - - with open(cache_file, "w") as f: - f.write(json.dumps(hours)) + + # Only cache if all 24 hours are present + if len(hours) == 24: + with open(cache_file, "w") as f: + f.write(json.dumps(hours)) + # If incomplete, do not cache, but return what is available return hours