Compare commits
3 Commits
ac637a6dae
...
v1.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
| b763aa1e9d | |||
| df210c66a0 | |||
| 560863d914 |
@@ -59,16 +59,3 @@ jobs:
|
||||
git tag "$VERSION"
|
||||
git push origin "$VERSION"
|
||||
|
||||
lint:
|
||||
runs-on: host
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run actionlint
|
||||
run: |
|
||||
# Trying to install it via apk if possible, otherwise fallback
|
||||
apk add --no-cache actionlint || echo "actionlint not found in apk"
|
||||
if command -v actionlint >/dev/null 2>&1; then
|
||||
actionlint .gitea/workflows/*.yml
|
||||
else
|
||||
echo "Skipping actionlint: not installed"
|
||||
fi
|
||||
|
||||
@@ -59,16 +59,3 @@ jobs:
|
||||
git tag "$VERSION"
|
||||
git push origin "$VERSION"
|
||||
|
||||
lint:
|
||||
runs-on: host
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run actionlint
|
||||
run: |
|
||||
# Trying to install it via apk if possible, otherwise fallback
|
||||
apk add --no-cache actionlint || echo "actionlint not found in apk"
|
||||
if command -v actionlint >/dev/null 2>&1; then
|
||||
actionlint .gitea/workflows/*.yml
|
||||
else
|
||||
echo "Skipping actionlint: not installed"
|
||||
fi
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
INFO: Will watch for changes in these directories: ['/Users/jhag/projects/babymonitor/backend']
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
INFO: Started reloader process [63655] using StatReload
|
||||
INFO: Started server process [63657]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: Application startup complete.
|
||||
WARNING: StatReload detected changes in 'app/main.py'. Reloading...
|
||||
INFO: Shutting down
|
||||
INFO: Waiting for application shutdown.
|
||||
INFO: Application shutdown complete.
|
||||
INFO: Finished server process [63657]
|
||||
INFO: Started server process [71064]
|
||||
INFO: Waiting for application startup.
|
||||
INFO: Application startup complete.
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker buildx build \
|
||||
--platform linux/arm64 \
|
||||
-t babymonitor-frontend:latest \
|
||||
-f frontend/Dockerfile \
|
||||
--load \
|
||||
frontend
|
||||
|
||||
docker buildx build \
|
||||
--platform linux/arm64 \
|
||||
-t babymonitor-backend:latest \
|
||||
-f backend/Dockerfile \
|
||||
--load \
|
||||
backend
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
podman save babymonitor-frontend:latest -o frontend.tar
|
||||
|
||||
podman save babymonitor-backend:latest -o backend.tar
|
||||
|
||||
scp frontend.tar backend.tar jacobhagstedt@raspberrypi.local:~/
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"failedTests": []
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
const adjustForTimezone = (utcMinutes) => {
|
||||
const offsetInMinutes = -120; // Simulate UTC+2
|
||||
return utcMinutes - offsetInMinutes;
|
||||
};
|
||||
|
||||
const formatMinutesAfter = (minutes, reference = "04:00") => {
|
||||
const [refHours, refMinutes] = reference.split(":").map(Number);
|
||||
const totalRefMinutes = refHours * 60 + refMinutes;
|
||||
const targetMinutes = totalRefMinutes + minutes;
|
||||
const normalizedMinutes = ((targetMinutes % 1440) + 1440) % 1440;
|
||||
const hours = Math.floor(normalizedMinutes / 60);
|
||||
const mins = normalizedMinutes % 60;
|
||||
return `${String(hours).padStart(2, "0")}:${String(mins).padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
// Case 1: Backend sends offset from 04:00 UTC (-5 minutes)
|
||||
const backendOffset = -5;
|
||||
const adjusted = adjustForTimezone(backendOffset);
|
||||
console.log("Adjusted offset:", adjusted); // Should be 115
|
||||
console.log("Formatted time:", formatMinutesAfter(adjusted, "04:00")); // Should be 05:55
|
||||
|
||||
// Case 2: Backend sends minutes from UTC midnight (235 minutes)
|
||||
const backendMidnightMins = 235;
|
||||
const adjusted2 = adjustForTimezone(backendMidnightMins);
|
||||
console.log("Adjusted midnight mins:", adjusted2); // Should be 355
|
||||
// If we use it as an offset from reference:
|
||||
console.log("Formatted time (as offset):", formatMinutesAfter(adjusted2, "04:00")); // Should be 09:55 (WRONG if it was supposed to be 05:55)
|
||||
|
||||
// If we want the offset from reference for Case 2:
|
||||
const refMins = 4 * 60;
|
||||
const correctOffset = adjusted2 - refMins; // 355 - 240 = 115
|
||||
console.log("Corrected offset (from midnight):", correctOffset); // 115
|
||||
console.log("Formatted time (corrected):", formatMinutesAfter(correctOffset, "04:00")); // 05:55
|
||||
@@ -20,5 +20,16 @@ exitcode '1': failure
|
||||
|
||||
- [ ] Editing a sleep session in "sleep sessions", when I click the edit button the times show up as UTC but should show as my local timezone
|
||||
- [ ] We should drop the "notes" column from the sleep_session table
|
||||
- [ ] Switch over to oh-my-opencode and try it out
|
||||
- [ ] Adda footer at the bottom of the app that shows the version (aka git tag) of the app
|
||||
- [x] Switch over to oh-my-opencode and try it out
|
||||
- [/] Adda footer at the bottom of the app that shows the version (aka git tag) of the app
|
||||
- [-] Fix race condition of gitea workflows
|
||||
|
||||
If both workflows run at the same time, a race condition occurs:
|
||||
1. Collision: Both workflows will query the latest tag (e.g., v1.0.5) and calculate the same next version (v1.0.6).
|
||||
2. The Winner: The first workflow to finish its push will successfully create the v1.0.6 tag on the remote.
|
||||
3. The Loser: The second workflow will attempt to push v1.0.6, but the remote will reject it because that tag already exists, causing the workflow to fail.
|
||||
This happens because both workflows share the same global tag namespace (v1.0.x). To fix this, you should differentiate them by using unique prefixes, such as backend-v1.0.x and frontend-v1.0.x.
|
||||
|
||||
--> Invalid. the containers are named differently.
|
||||
|
||||
- [x] remove the lint form the gitea workflows added by the agent. makes no sense there
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import datetime
|
||||
|
||||
def verify_real_wakeup_with_mock_api():
|
||||
# --- DATA FROM DATABASE (REAL) ---
|
||||
# session id 1 wakeup: 2026-05-27 03:17:00 UTC
|
||||
wakeup_utc = datetime.datetime(2026, 5, 27, 3, 17)
|
||||
wakeup_utc_mins = wakeup_utc.hour * 60 + wakeup_utc.minute
|
||||
|
||||
# --- SETUP PARAMETERS (CLIENT SIDE) ---
|
||||
# User is in Stockholm (+2)
|
||||
offset_in_minutes = -120
|
||||
local_ref_str = "04:00"
|
||||
local_ref_mins = 4 * 60
|
||||
|
||||
print(f"1. Database Wakeup (UTC): {wakeup_utc} ({wakeup_utc_mins}m)")
|
||||
print(f"2. Client Local Reference: {local_ref_str} ({local_ref_mins}m)")
|
||||
print(f"3. Client Timezone Offset: {offset_in_minutes}m")
|
||||
|
||||
# --- STEP 1: SIMULATE API CALL (CLIENT SIDE) ---
|
||||
def localHHMMToUTCHHMM(hhmm, offset):
|
||||
[h, m] = map(int, hhmm.split(":"))
|
||||
total_mins_utc = (h * 60 + m) + offset
|
||||
norm = ((total_mins_utc % 1440) + 1440) % 1440
|
||||
return f"{norm // 60:02d}:{norm % 60:02d}"
|
||||
|
||||
utc_ref = localHHMMToUTCHHMM(local_ref_str, offset_in_minutes)
|
||||
print(f"4. Simulated API call with wakeup_ref (converted to UTC): {utc_ref}")
|
||||
|
||||
# --- STEP 2: SIMULATE BACKEND (SERVER SIDE) ---
|
||||
# Backend receives utc_ref and calculates delta: wakeup_utc - utc_ref
|
||||
utc_ref_mins = (int(utc_ref.split(":")[0]) * 60 + int(utc_ref.split(":")[1]))
|
||||
backend_delta = wakeup_utc_mins - utc_ref_mins
|
||||
print(f"5. Backend returns delta: {backend_delta}")
|
||||
|
||||
# --- STEP 3: SIMULATE COMPONENT RENDERING (CLIENT SIDE) ---
|
||||
# Component receives delta and uses it directly as offsetFromRef.
|
||||
offset_from_ref = backend_delta
|
||||
|
||||
def formatMinutesAfter(minutes, ref):
|
||||
[rh, rm] = map(int, ref.split(":"))
|
||||
target = (rh * 60 + rm) + minutes
|
||||
norm = ((target % 1440) + 1440) % 1440
|
||||
return f"{norm // 60:02d}:{norm % 60:02d}"
|
||||
|
||||
displayed_time = formatMinutesAfter(offset_from_ref, local_ref_str)
|
||||
print(f"6. Component renders time: {displayed_time}")
|
||||
|
||||
# --- ASSERTION ---
|
||||
expected_local_time = "05:17"
|
||||
print(f"\nASSERTION: Expected {expected_local_time}, Actual {displayed_time}")
|
||||
if displayed_time == expected_local_time:
|
||||
print("VERIFICATION SUCCESSFUL!")
|
||||
else:
|
||||
print("VERIFICATION FAILED!")
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
verify_real_wakeup_with_mock_api()
|
||||
@@ -1,57 +0,0 @@
|
||||
import datetime
|
||||
|
||||
def solve():
|
||||
# Scenario: User in Stockholm (UTC+2)
|
||||
# Local Wakeup Reference: 04:00
|
||||
# Actual Wakeup Time (UTC): 03:17
|
||||
|
||||
offset_minutes = -120 # Stockholm UTC+2 -> getTimezoneOffset() is -120
|
||||
|
||||
wakeup_ref_local = "04:00"
|
||||
wakeup_ref_mins_local = 4 * 60
|
||||
|
||||
# Let's assume the backend calculates: wakeup_minutes = (UTC_wakeup - UTC_ref)
|
||||
# where it treats the provided ref as if it were in UTC.
|
||||
|
||||
# Case 1: BUGGY (Frontend sends Local ref "04:00" to backend)
|
||||
# Backend treats 04:00 as UTC.
|
||||
utc_wakeup = 3 * 60 + 17 # 03:17 UTC
|
||||
wakeup_minutes_from_backend = utc_wakeup - wakeup_ref_mins_local # 197 - 240 = -43
|
||||
print(f"BUGGY: Backend returns wakeup_minutes (assuming ref 04:00 is UTC): {wakeup_minutes_from_backend}")
|
||||
|
||||
# Frontend (current) tries to fix it with adjustForTimezone:
|
||||
received_wakeup_minutes = wakeup_minutes_from_backend
|
||||
# offsetFromRef = getLocalOffsetFromReference(received, "04:00")
|
||||
# localMins = adjustForTimezone(received) = received - offset_minutes
|
||||
# offsetFromRef = localMins - ref_mins_local
|
||||
|
||||
def adjustForTimezone(utc_min):
|
||||
return utc_min - offset_minutes
|
||||
|
||||
def getLocalOffsetFromReference(utc_min, ref_mins):
|
||||
local_m = adjustForTimezone(utc_min)
|
||||
return local_m - ref_mins
|
||||
|
||||
offset_from_ref = getLocalOffsetFromReference(received_wakeup_minutes, wakeup_ref_mins_local)
|
||||
print(f"BUGGY: Frontend calculates offsetFromRef: {offset_from_ref}")
|
||||
# formatMinutesAfter(offset, ref) -> ref + offset
|
||||
print(f"BUGGY: Displayed time (ref + offset): {wakeup_ref_mins_local + offset_from_ref}")
|
||||
|
||||
# Case 2: CORRECT (Frontend sends UTC ref "02:00" to backend)
|
||||
# Stockholm +2 means 04:00 Local is 02:00 UTC.
|
||||
utc_wakeup_ref = wakeup_ref_mins_local + offset_minutes # 240 - 120 = 120
|
||||
wakeup_minutes_from_backend_correct = utc_wakeup - utc_wakeup_ref # 197 - 120 = 77
|
||||
print(f"\nCORRECT: Backend returns wakeup_minutes (assuming ref 02:00 is UTC): {wakeup_minutes_from_backend_correct}")
|
||||
|
||||
# Frontend receives 77.
|
||||
# If it's a delta, the component should use it directly as offsetFromRef.
|
||||
# Or if we want to keep existing logic, we have to realize 77 is ALREADY the delta.
|
||||
# But the component thinks it's "minutes from midnight".
|
||||
# This is a mismatch.
|
||||
|
||||
print("---")
|
||||
print(f"Target Local Wakeup: 05:17 ({3 * 60 + 17 + abs(offset_minutes)})")
|
||||
print(f"Target Local Ref: 04:00 ({wakeup_ref_mins_local})")
|
||||
print(f"Target Local Delta: {317 - 240} = 77")
|
||||
|
||||
solve()
|
||||
@@ -1,31 +0,0 @@
|
||||
# Plan: Fix Timezone Handling in Wakeup Times Module
|
||||
|
||||
## Problem Description
|
||||
The "Wakeup Times" chart (in `frontend/src/components/stats/WakeupPlot.tsx`) displays wakeup times incorrectly due to a timezone mismatch.
|
||||
- **Observation**: If a baby wakes up at `05:55` local time (e.g., in UTC+2), the chart shows `03:55`.
|
||||
- **Cause**: The backend stores times in UTC. The frontend currently receives minutes relative to UTC midnight and renders them directly using `formatMinutesAfter` without applying the client's local timezone offset.
|
||||
|
||||
## Investigation Steps
|
||||
1. **Backend Verification**:
|
||||
- Examine `backend/app/routers/stats.py` (or equivalent) and the CRUD logic to confirm that `wakeup_minutes` is calculated based on UTC midnight.
|
||||
- Verify how the backend handles the `wakeup_ref` and `bedtime_ref` parameters.
|
||||
2. **Frontend Logic Audit**:
|
||||
- Trace the data flow from `fetchWakeSleepTimes` -> `useStats` -> `WakeupPlot.tsx`.
|
||||
- Confirm that `wakeup_minutes` is indeed a raw offset from UTC midnight.
|
||||
|
||||
## Implementation Plan
|
||||
1. **Calculate Local Offset**: Use `new Date().getTimezoneOffset()` in the frontend to determine the difference between UTC and local time.
|
||||
2. **Adjust Minutes**:
|
||||
- In `WakeupPlot.tsx`, modify the mapping logic to adjust `day.wakeup_minutes` by the local timezone offset before it is passed to `formatMinutesAfter`.
|
||||
- Formula: `local_minutes = utc_minutes - (timezone_offset_in_minutes)`.
|
||||
*(Note: `getTimezoneOffset()` returns minutes positive for west of UTC, negative for east, so subtraction is correct to add offset to UTC).*
|
||||
3. **Refactor Utilities (if needed)**: If the logic is used elsewhere, consider moving timezone-aware formatting to `frontend/src/utils/time.ts`.
|
||||
|
||||
## Verification Plan
|
||||
1. **Manual Verification**:
|
||||
- Mock a response from the backend with a known UTC time.
|
||||
- Check if the rendered string matches the expected local time.
|
||||
2. **Automated Testing**:
|
||||
- Add/update a test case in `frontend/tests/e2e/stats.spec.ts` (or a unit test in `frontend/tests/`) that simulates different timezone offsets to ensure the display is correct.
|
||||
3. **Regression Check**:
|
||||
- Ensure `BedtimePlot.tsx` and other time-related components are not negatively impacted by changes to shared utilities.
|
||||
Reference in New Issue
Block a user