Skip to content

Commit 85fa904

Browse files
itsDNNSclaude
andcommitted
Add timezone display for snapshot time, change history default to unlimited
Snapshot time hint now shows the server timezone and warns if the browser timezone differs. History days default changed from 7 to 0 (keep all snapshots) so users don't lose data unexpectedly. All docker-compose examples include a commented TZ environment variable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 45c89d2 commit 85fa904

File tree

10 files changed

+56
-13
lines changed

10 files changed

+56
-13
lines changed

INSTALL.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ services:
9797
- "8765:8765"
9898
volumes:
9999
- docsight_data:/data
100+
# environment:
101+
# - TZ=Europe/Berlin # set container timezone for snapshot scheduling
100102

101103
volumes:
102104
docsight_data:
@@ -146,6 +148,8 @@ services:
146148
- "8765:8765"
147149
volumes:
148150
- docsight_data:/data
151+
# environment:
152+
# - TZ=Europe/Berlin # set container timezone for snapshot scheduling
149153
150154
volumes:
151155
docsight_data:
@@ -187,6 +191,8 @@ services:
187191
- "8765:8765"
188192
volumes:
189193
- docsight_data:/data
194+
# environment:
195+
# - TZ=Europe/Berlin # set container timezone for snapshot scheduling
190196
191197
volumes:
192198
docsight_data:
@@ -244,8 +250,8 @@ Click **Test Connection** to verify DOCSight can reach your router. If successfu
244250
|---|---|---|
245251
| **ISP** | Your internet provider name (for reports) | - |
246252
| **Poll Interval** | How often to read channel data (seconds) | `300` (5 min) |
247-
| **History Days** | How many days of snapshots to keep | `7` |
248-
| **Snapshot Time** | When to save the daily snapshot | `03:00` |
253+
| **History Days** | How many days of snapshots to keep (0 = unlimited) | `0` (keep all) |
254+
| **Snapshot Time** | When to save the daily snapshot (server timezone, set `TZ` env var to change) | `06:00` |
249255

250256
### Advanced: MQTT for Home Assistant (optional)
251257

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ Copy `.env.example` to `.env` and edit:
8383
| `MQTT_TOPIC_PREFIX` | `docsight` | MQTT topic prefix |
8484
| `POLL_INTERVAL` | `300` | Polling interval in seconds |
8585
| `WEB_PORT` | `8765` | Web UI port |
86-
| `HISTORY_DAYS` | `7` | Snapshot retention in days |
86+
| `HISTORY_DAYS` | `0` | Snapshot retention in days (0 = unlimited) |
8787
| `ADMIN_PASSWORD` | - | Web UI password (optional) |
8888

8989
</details>

app/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"mqtt_topic_prefix": "docsight",
2929
"poll_interval": 300,
3030
"web_port": 8765,
31-
"history_days": 7,
31+
"history_days": 0,
3232
"snapshot_time": "06:00",
3333
"theme": "dark",
3434
"language": "en",

app/i18n.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,10 @@
112112
"poll_interval": "Poll Interval",
113113
"interval_sec": "Interval (seconds)",
114114
"history_days": "History (days)",
115-
"history_hint": "How long snapshots are stored",
115+
"history_hint": "0 = keep all snapshots",
116116
"snapshot_time": "Daily Snapshot (time)",
117117
"snapshot_hint": "Reference time for daily comparisons and trends",
118+
"snapshot_browser_tz": "Your timezone",
118119
"advanced_mqtt": "Advanced: MQTT / Home Assistant",
119120
"optional": "Optional",
120121
"mqtt_broker": "MQTT Broker",
@@ -273,9 +274,10 @@
273274
"poll_interval": "Abfrageintervall",
274275
"interval_sec": "Intervall (Sekunden)",
275276
"history_days": "Historie (Tage)",
276-
"history_hint": "Wie lange Snapshots gespeichert werden",
277+
"history_hint": "0 = alle Snapshots behalten",
277278
"snapshot_time": "Tages-Snapshot (Uhrzeit)",
278279
"snapshot_hint": "Referenz-Zeitpunkt fuer Tagesvergleiche und Trends",
280+
"snapshot_browser_tz": "Deine Zeitzone",
279281
"advanced_mqtt": "Erweitert: MQTT / Home Assistant",
280282
"optional": "Optional",
281283
"mqtt_broker": "MQTT Broker",

app/storage.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ def get_intraday_data(self, date):
141141
return results
142142

143143
def _cleanup(self):
144-
"""Delete snapshots older than max_days."""
144+
"""Delete snapshots older than max_days. 0 = keep all."""
145+
if self.max_days <= 0:
146+
return
145147
cutoff = (datetime.now() - timedelta(days=self.max_days)).strftime(
146148
"%Y-%m-%dT%H:%M:%S"
147149
)

app/templates/settings.html

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,13 @@ <h2>{{ t.general }}</h2>
233233
</div>
234234
<div class="form-row">
235235
<label for="history_days">{{ t.history_days }}</label>
236-
<input type="number" id="history_days" name="history_days" value="{{ config.history_days }}" min="1">
236+
<input type="number" id="history_days" name="history_days" value="{{ config.history_days }}" min="0">
237+
<span class="hint">{{ t.history_hint }}</span>
237238
</div>
238239
<div class="form-row">
239240
<label for="snapshot_time">{{ t.snapshot_time }}</label>
240241
<input type="time" id="snapshot_time" name="snapshot_time" value="{{ config.snapshot_time }}">
241-
<span class="hint">{{ t.snapshot_hint }}</span>
242+
<span class="hint" id="snapshot-tz-hint">{{ t.snapshot_hint }} ({{ server_tz }})</span>
242243
</div>
243244
<div class="form-row">
244245
<label for="language">{{ t.language }}</label>
@@ -398,6 +399,15 @@ <h2>{{ t.general }}</h2>
398399
errEl.style.display = 'block';
399400
});
400401
});
402+
403+
(function() {
404+
var serverTz = '{{ server_tz }}';
405+
var browserTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
406+
if (browserTz && browserTz !== serverTz) {
407+
var el = document.getElementById('snapshot-tz-hint');
408+
el.textContent = el.textContent + ' — ' + T.snapshot_browser_tz + ': ' + browserTz;
409+
}
410+
})();
401411
</script>
402412

403413
</body>

app/templates/setup.html

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,13 @@ <h2><span class="step-num">2</span>{{ t.general }}</h2>
246246
</div>
247247
<div class="form-row">
248248
<label for="history_days">{{ t.history_days }}</label>
249-
<input type="number" id="history_days" name="history_days" value="{{ config.history_days }}" min="1" max="90">
249+
<input type="number" id="history_days" name="history_days" value="{{ config.history_days }}" min="0" max="3650">
250250
<span class="hint">{{ t.history_hint }}</span>
251251
</div>
252252
<div class="form-row">
253253
<label for="snapshot_time">{{ t.snapshot_time }}</label>
254254
<input type="time" id="snapshot_time" name="snapshot_time" value="{{ config.snapshot_time }}">
255-
<span class="hint">{{ t.snapshot_hint }}</span>
255+
<span class="hint" id="snapshot-tz-hint">{{ t.snapshot_hint }} ({{ server_tz }})</span>
256256
</div>
257257
</div>
258258
</div>
@@ -428,6 +428,15 @@ <h2>{{ t.mqtt_broker }}</h2>
428428
errEl.style.display = 'block';
429429
});
430430
});
431+
432+
(function() {
433+
var serverTz = '{{ server_tz }}';
434+
var browserTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
435+
if (browserTz && browserTz !== serverTz) {
436+
var el = document.getElementById('snapshot-tz-hint');
437+
el.textContent = el.textContent + ' — ' + T.snapshot_browser_tz + ': ' + browserTz;
438+
}
439+
})();
431440
</script>
432441

433442
</body>

app/web.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
from .config import POLL_MIN, POLL_MAX, PASSWORD_MASK, SECRET_KEYS
1515
from .i18n import get_translations, LANGUAGES
1616

17+
def _server_timezone():
18+
"""Return the server's current timezone abbreviation (e.g. 'UTC', 'CET')."""
19+
return datetime.now().astimezone().strftime("%Z") or time.tzname[0] or "UTC"
20+
1721
log = logging.getLogger("docsis.web")
1822

1923
app = Flask(__name__, template_folder="templates")
@@ -216,7 +220,7 @@ def setup():
216220
config = _config_manager.get_all(mask_secrets=True) if _config_manager else {}
217221
lang = _get_lang()
218222
t = get_translations(lang)
219-
return render_template("setup.html", config=config, poll_min=POLL_MIN, poll_max=POLL_MAX, t=t, lang=lang, languages=LANGUAGES)
223+
return render_template("setup.html", config=config, poll_min=POLL_MIN, poll_max=POLL_MAX, t=t, lang=lang, languages=LANGUAGES, server_tz=_server_timezone())
220224

221225

222226
@app.route("/settings")
@@ -226,7 +230,7 @@ def settings():
226230
theme = _config_manager.get_theme() if _config_manager else "dark"
227231
lang = _get_lang()
228232
t = get_translations(lang)
229-
return render_template("settings.html", config=config, theme=theme, poll_min=POLL_MIN, poll_max=POLL_MAX, t=t, lang=lang, languages=LANGUAGES)
233+
return render_template("settings.html", config=config, theme=theme, poll_min=POLL_MIN, poll_max=POLL_MAX, t=t, lang=lang, languages=LANGUAGES, server_tz=_server_timezone())
230234

231235

232236
@app.route("/api/config", methods=["POST"])

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ services:
77
- "8765:8765"
88
volumes:
99
- docsight_data:/data
10+
# environment:
11+
# - TZ=Europe/Berlin # set container timezone for snapshot scheduling
1012
# env_file: .env # uncomment if using environment variables
1113

1214
volumes:

tests/test_storage.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,11 @@ def test_intraday_data(self, storage, sample_analysis):
5656
def test_empty_storage(self, storage):
5757
assert storage.get_snapshot_list() == []
5858
assert storage.get_dates_with_data() == []
59+
60+
def test_unlimited_retention(self, tmp_path, sample_analysis):
61+
"""max_days=0 should keep all snapshots (no cleanup)."""
62+
db_path = str(tmp_path / "unlimited.db")
63+
s = SnapshotStorage(db_path, max_days=0)
64+
s.save_snapshot(sample_analysis)
65+
s.save_snapshot(sample_analysis)
66+
assert len(s.get_snapshot_list()) == 2

0 commit comments

Comments
 (0)