This commit is contained in:
2026-04-26 14:44:46 +02:00
parent 2136c10f69
commit 8ba22fc4a0
5 changed files with 75 additions and 45 deletions
+21 -30
View File
@@ -110,43 +110,34 @@ async def get_wake_sleep_times(
except ValueError:
return {"days": []}
sessions, _ = await get_sessions(session, from_date=from_date, to_date=to_date)
# Fetch sessions starting from one day before to catch night sleep ending on from_date
adj_from_date = from_date - timedelta(days=1)
sessions, _ = await get_sessions(session, from_date=adj_from_date, to_date=to_date)
days_data = []
current_date = from_date.date()
while current_date <= to_date.date():
date_str = current_date.strftime("%Y-%m-%d")
day_sessions = [
s
for s in sessions
if s.start_time.date() == current_date
or (s.end_time and s.end_time.date() == current_date)
]
wakeup_min = None
bedtime_min = None
if day_sessions:
# Find first wakeup after wakeup_ref
wakeups = []
for s in day_sessions:
if s.end_time and s.end_time.time() >= w_ref_time:
diff = (
s.end_time - datetime.combine(current_date, w_ref_time)
).total_seconds() / 60.0
wakeups.append(int(diff))
if wakeups:
wakeup_min = min(wakeups)
# Find last bedtime before bedtime_ref
bedtimes = []
for s in day_sessions:
if s.start_time.time() <= b_ref_time:
diff = (
s.start_time - datetime.combine(current_date, b_ref_time)
).total_seconds() / 60.0
bedtimes.append(int(diff))
if bedtimes:
bedtime_min = max(bedtimes)
midnight_today = datetime.combine(current_date, time(0, 0))
night_session = None
for s in sessions:
# A session that runs over midnight into current_date
if s.start_time < midnight_today and s.end_time and s.end_time >= midnight_today:
night_session = s
break
if night_session:
# Bedtime offset from b_ref_time on the day it started
s_start_date = night_session.start_time.date()
ref_datetime_bedtime = datetime.combine(s_start_date, b_ref_time)
bedtime_min = int((night_session.start_time - ref_datetime_bedtime).total_seconds() / 60.0)
# Wakeup offset from w_ref_time on the current day
ref_datetime_wakeup = datetime.combine(current_date, w_ref_time)
wakeup_min = int((night_session.end_time - ref_datetime_wakeup).total_seconds() / 60.0)
days_data.append(
{
@@ -18,8 +18,8 @@ interface WakeupBedtimeProps {
bedtime_ref: string;
}
const BedtimePlot: React.FC<WakeupBedtimeProps> = ({ from_date, to_date, bedtime_ref }) => {
const { wakeupBedtime, isLoading } = useStats(from_date, to_date, '07:00', '20:00');
const BedtimePlot: React.FC<WakeupBedtimeProps> = ({ from_date, to_date, wakeup_ref, bedtime_ref }) => {
const { wakeupBedtime, isLoading } = useStats(from_date, to_date, '07:00', '20:00', wakeup_ref, bedtime_ref);
const [chartData, setChartData] = useState<{ date: string; minutes: number; time: string }[]>([]);
useEffect(() => {
+16 -11
View File
@@ -40,9 +40,9 @@ const DailySummary: React.FC<DailySummaryProps> = ({ from_date, to_date, day_sta
const data = dailySummary.days.map((day) => ({
date: day.date,
daySleep: day.day_minutes,
nightSleep: day.night_minutes,
total: day.total_minutes,
daySleep: day.day_minutes / 60,
nightSleep: day.night_minutes / 60,
total: day.total_minutes / 60,
}));
setChartData(data.reverse());
@@ -58,9 +58,9 @@ const DailySummary: React.FC<DailySummaryProps> = ({ from_date, to_date, day_sta
);
const count = data.length;
setAvgStats({
avgTotal: Math.round(totals.total / count),
avgDay: Math.round(totals.day / count),
avgNight: Math.round(totals.night / count),
avgTotal: totals.total / count,
avgDay: totals.day / count,
avgNight: totals.night / count,
count,
});
}
@@ -81,7 +81,12 @@ const DailySummary: React.FC<DailySummaryProps> = ({ from_date, to_date, day_sta
margin={{ top: 5, right: 30, left: 60, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis type="number" tick={{ fontSize: 12 }} tickLine={false} />
<XAxis
type="number"
tickFormatter={(value) => `${Math.round(value)}h`}
tick={{ fontSize: 12 }}
tickLine={false}
/>
<YAxis
type="category"
dataKey="date"
@@ -91,7 +96,7 @@ const DailySummary: React.FC<DailySummaryProps> = ({ from_date, to_date, day_sta
/>
<Tooltip
formatter={(value: number, name: string) => [
formatMinutes(value),
formatMinutes(Math.round(value * 60)),
name === 'daySleep' ? 'Day' : 'Night',
]}
/>
@@ -102,15 +107,15 @@ const DailySummary: React.FC<DailySummaryProps> = ({ from_date, to_date, day_sta
<div className="mt-4 grid grid-cols-3 gap-4 text-center">
<div className="p-3 bg-gray-50 rounded-lg">
<div className="text-xs text-gray-500">Avg Total</div>
<div className="text-lg font-semibold text-gray-800">{formatMinutes(avgStats.avgTotal)}</div>
<div className="text-lg font-semibold text-gray-800">{formatMinutes(Math.round(avgStats.avgTotal * 60))}</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="text-xs text-gray-500">Avg Day</div>
<div className="text-lg font-semibold text-gray-800">{formatMinutes(avgStats.avgDay)}</div>
<div className="text-lg font-semibold text-gray-800">{formatMinutes(Math.round(avgStats.avgDay * 60))}</div>
</div>
<div className="p-3 bg-gray-50 rounded-lg">
<div className="text-xs text-gray-500">Avg Night</div>
<div className="text-lg font-semibold text-gray-800">{formatMinutes(avgStats.avgNight)}</div>
<div className="text-lg font-semibold text-gray-800">{formatMinutes(Math.round(avgStats.avgNight * 60))}</div>
</div>
</div>
</div>
+2 -2
View File
@@ -18,8 +18,8 @@ interface WakeupBedtimeProps {
bedtime_ref: string;
}
const WakeupPlot: React.FC<WakeupBedtimeProps> = ({ from_date, to_date, wakeup_ref }) => {
const { wakeupBedtime, isLoading } = useStats(from_date, to_date, '07:00', '20:00');
const WakeupPlot: React.FC<WakeupBedtimeProps> = ({ from_date, to_date, wakeup_ref, bedtime_ref }) => {
const { wakeupBedtime, isLoading } = useStats(from_date, to_date, '07:00', '20:00', wakeup_ref, bedtime_ref);
const [chartData, setChartData] = useState<{ date: string; minutes: number; time: string }[]>([]);
useEffect(() => {
+34
View File
@@ -0,0 +1,34 @@
# Investigation Plan: Bedtime Graph Issue
## Objective
Investigate why the "Bedtime" graph shows unexpected times when a specific `bedtime_ref` is set.
## Current Understanding
- The `BedtimePlot.tsx` component displays a graph where the Y-axis (or values) are formatted using `formatMinutesAfter(value, bedtime_ref)`.
- The data comes from the `/api/stats/wake-sleep-times` endpoint.
- The backend calculates `bedtime_minutes` based on sessions that started on or before the `bedtime_ref`.
## Investigation Steps
### Step 1: Analyze Backend Logic
- [ ] Examine `get_wake_sleep_times` in `backend/app/routers/stats.py` to understand how `bedtime_minutes` is calculated.
- [ ] Verify if the logic correctly identifies "bedtime" for various sleep session scenarios (e.g., sessions crossing midnight).
- [ ] Identify potential edge cases where the `s.start_time.time() <= b_ref_time` condition might be problematic.
### Step 2: Analyze Frontend Logic
- [ ] Examine `BedtimePlot.tsx` and `useStats.ts` to see how the data is consumed and formatted.
- [ ] Understand how `formatMinutesAfter` interacts with the values returned by the backend.
### Step 3: Data Simulation & Verification
- [ ] Create a set of test cases (mocked data) representing different sleep scenarios.
- [ ] Manually calculate the expected `bedtime_minutes` for these scenarios using the current logic.
- [ ] Compare with what the user reported to confirm the bug's nature.
### Step 4: Propose and Implement Fixes
- [ ] Refine the backend logic to better represent "bedtime" if current logic is insufficient.
- [ ] Ensure the frontend correctly displays the intended time.
- [ ] Verify that changes do not break other parts of the stats page (e.g., Wakeup times).
## Future Tasks
- [ ] Run tests to ensure correctness after implementation.
- [ ] Finalize documentation/explanation of the fix.