Files
HouseHoldKeaper/lib/features/rooms/data/rooms_dao.dart
Jean-Luc Makiola b2f14dcd97 feat(08-01): add isActive filters to CalendarDao, DailyPlanDao, RoomsDao
- CalendarDao: filter all 6 task queries (watchTasksForDate,
  watchTasksForDateInRoom, watchOverdueTasks, watchOverdueTasksInRoom,
  getTaskCount, getTaskCountInRoom) by isActive=true
- DailyPlanDao: filter all 3 queries (watchAllTasksWithRoomName,
  getOverdueAndTodayTaskCount, getOverdueTaskCount) by isActive=true
- RoomsDao: filter watchRoomWithStats task query by isActive=true
- Update migration test: add schema_v3.dart, test v1->v3 and v2->v3 paths
- Update database_test schemaVersion assertion to expect 3
- Fix test helpers in home_screen_test and task_list_screen_test to pass isActive=true
2026-03-18 20:56:34 +01:00

130 lines
3.8 KiB
Dart

import 'package:drift/drift.dart';
import '../../../core/database/database.dart';
part 'rooms_dao.g.dart';
/// Stats for a room including task counts and cleanliness ratio.
class RoomWithStats {
final Room room;
final int totalTasks;
final int dueTasks;
final int overdueCount;
final double cleanlinessRatio;
const RoomWithStats({
required this.room,
required this.totalTasks,
required this.dueTasks,
required this.overdueCount,
required this.cleanlinessRatio,
});
}
@DriftAccessor(tables: [Rooms, Tasks, TaskCompletions])
class RoomsDao extends DatabaseAccessor<AppDatabase> with _$RoomsDaoMixin {
RoomsDao(super.attachedDatabase);
/// Watch all rooms ordered by sortOrder.
Stream<List<Room>> watchAllRooms() {
return (select(rooms)..orderBy([(r) => OrderingTerm.asc(r.sortOrder)]))
.watch();
}
/// Watch all rooms with computed task stats.
///
/// Cleanliness ratio = (totalTasks - overdueCount) / totalTasks.
/// 1.0 when no tasks are overdue, 0.0 when all are overdue.
/// Rooms with no tasks have ratio 1.0.
Stream<List<RoomWithStats>> watchRoomWithStats({DateTime? today}) {
final now = today ?? DateTime.now();
final todayDateOnly = DateTime(now.year, now.month, now.day);
return watchAllRooms().asyncMap((roomList) async {
final stats = <RoomWithStats>[];
for (final room in roomList) {
final taskList = await (select(tasks)
..where(
(t) => t.roomId.equals(room.id) & t.isActive.equals(true),
))
.get();
final totalTasks = taskList.length;
var overdueCount = 0;
var dueTasks = 0;
for (final task in taskList) {
final dueDate = DateTime(
task.nextDueDate.year,
task.nextDueDate.month,
task.nextDueDate.day,
);
if (dueDate.isBefore(todayDateOnly) ||
dueDate.isAtSameMomentAs(todayDateOnly)) {
dueTasks++;
}
if (dueDate.isBefore(todayDateOnly)) {
overdueCount++;
}
}
final ratio =
totalTasks == 0 ? 1.0 : (totalTasks - overdueCount) / totalTasks;
stats.add(RoomWithStats(
room: room,
totalTasks: totalTasks,
dueTasks: dueTasks,
overdueCount: overdueCount,
cleanlinessRatio: ratio,
));
}
return stats;
});
}
/// Insert a new room. Returns the auto-generated id.
Future<int> insertRoom(RoomsCompanion room) => into(rooms).insert(room);
/// Update an existing room. Returns true if a row was updated.
Future<bool> updateRoom(Room room) => update(rooms).replace(room);
/// Delete a room and cascade to its tasks and completions.
Future<void> deleteRoom(int roomId) {
return transaction(() async {
// Get all task IDs for this room
final taskList = await (select(tasks)
..where((t) => t.roomId.equals(roomId)))
.get();
// Delete completions for each task
for (final task in taskList) {
await (delete(taskCompletions)
..where((c) => c.taskId.equals(task.id)))
.go();
}
// Delete tasks
await (delete(tasks)..where((t) => t.roomId.equals(roomId))).go();
// Delete room
await (delete(rooms)..where((r) => r.id.equals(roomId))).go();
});
}
/// Reorder rooms by updating sortOrder for each room in the list.
Future<void> reorderRooms(List<int> roomIds) {
return transaction(() async {
for (var i = 0; i < roomIds.length; i++) {
await (update(rooms)..where((r) => r.id.equals(roomIds[i])))
.write(RoomsCompanion(sortOrder: Value(i)));
}
});
}
/// Get a single room by its id.
Future<Room> getRoomById(int id) {
return (select(rooms)..where((r) => r.id.equals(id))).getSingle();
}
}