From da270e545733d0f6bbcf0918ddc7f29689ab0922 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 15 Mar 2026 21:52:05 +0100 Subject: [PATCH] feat(02-01): German task templates for 14 room types with detectRoomType - Create TaskTemplate class with name, intervalType, intervalDays, effortLevel - Add roomTemplates const map with 3-6 templates per room type (14 total) - Implement detectRoomType with case-insensitive matching and alias support - Room types: kueche, badezimmer, schlafzimmer, wohnzimmer, flur, buero, garage, balkon, waschkueche, keller, kinderzimmer, gaestezimmer, esszimmer, garten - All 11 template tests passing Co-Authored-By: Claude Opus 4.6 --- .../templates/data/task_templates.dart | 371 ++++++++++++++++++ .../templates/task_templates_test.dart | 111 ++++++ 2 files changed, 482 insertions(+) create mode 100644 lib/features/templates/data/task_templates.dart create mode 100644 test/features/templates/task_templates_test.dart diff --git a/lib/features/templates/data/task_templates.dart b/lib/features/templates/data/task_templates.dart new file mode 100644 index 0000000..2d2b748 --- /dev/null +++ b/lib/features/templates/data/task_templates.dart @@ -0,0 +1,371 @@ +import '../../tasks/domain/effort_level.dart'; +import '../../tasks/domain/frequency.dart'; + +/// A bundled task template with German name, interval, and effort level. +class TaskTemplate { + final String name; + final String? description; + final IntervalType intervalType; + final int intervalDays; + final EffortLevel effortLevel; + + const TaskTemplate({ + required this.name, + this.description, + required this.intervalType, + this.intervalDays = 1, + required this.effortLevel, + }); +} + +/// German-language task templates for all 14 room types. +/// +/// Each room type has 3-6 realistic household tasks with appropriate +/// frequencies and effort levels. +const Map> roomTemplates = { + 'kueche': [ + TaskTemplate( + name: 'Abspuelen', + intervalType: IntervalType.daily, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Herd reinigen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Kuehlschrank reinigen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Backofen reinigen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.high, + ), + TaskTemplate( + name: 'Muell rausbringen', + intervalType: IntervalType.everyNDays, + intervalDays: 2, + effortLevel: EffortLevel.low, + ), + ], + 'badezimmer': [ + TaskTemplate( + name: 'Toilette putzen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Spiegel reinigen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Dusche reinigen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Waschbecken reinigen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Badezimmerboden wischen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + ], + 'schlafzimmer': [ + TaskTemplate( + name: 'Bettwasche wechseln', + intervalType: IntervalType.biweekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Staubsaugen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Staub wischen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Fenster putzen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + ], + 'wohnzimmer': [ + TaskTemplate( + name: 'Staubsaugen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Staub wischen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Kissen aufschuetteln', + intervalType: IntervalType.daily, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Fenster putzen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + ], + 'flur': [ + TaskTemplate( + name: 'Boden wischen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Schuhe aufraumen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Garderobe aufraeumen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.low, + ), + ], + 'buero': [ + TaskTemplate( + name: 'Schreibtisch aufraeumen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Staubsaugen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Bildschirm reinigen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Papierkorb leeren', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + ], + 'garage': [ + TaskTemplate( + name: 'Boden fegen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Aufraeumen', + intervalType: IntervalType.quarterly, + effortLevel: EffortLevel.high, + ), + TaskTemplate( + name: 'Werkzeug sortieren', + intervalType: IntervalType.quarterly, + effortLevel: EffortLevel.medium, + ), + ], + 'balkon': [ + TaskTemplate( + name: 'Boden fegen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Pflanzen giessen', + intervalType: IntervalType.everyNDays, + intervalDays: 2, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Gelaender reinigen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Moebel reinigen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + ], + 'waschkueche': [ + TaskTemplate( + name: 'Waschmaschine reinigen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Trockner reinigen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Boden wischen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Waschmittel auffuellen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.low, + ), + ], + 'keller': [ + TaskTemplate( + name: 'Staubsaugen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Aufraeumen', + intervalType: IntervalType.quarterly, + effortLevel: EffortLevel.high, + ), + TaskTemplate( + name: 'Luftentfeuchter pruefen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.low, + ), + ], + 'kinderzimmer': [ + TaskTemplate( + name: 'Spielzeug aufraeumen', + intervalType: IntervalType.daily, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Staubsaugen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Bettwasche wechseln', + intervalType: IntervalType.biweekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Staub wischen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + ], + 'gaestezimmer': [ + TaskTemplate( + name: 'Staubsaugen', + intervalType: IntervalType.biweekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Bettwasche wechseln', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Staub wischen', + intervalType: IntervalType.biweekly, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Lueften', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.low, + ), + ], + 'esszimmer': [ + TaskTemplate( + name: 'Tisch abwischen', + intervalType: IntervalType.daily, + effortLevel: EffortLevel.low, + ), + TaskTemplate( + name: 'Staubsaugen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Stuehle reinigen', + intervalType: IntervalType.monthly, + effortLevel: EffortLevel.medium, + ), + ], + 'garten': [ + TaskTemplate( + name: 'Rasen maehen', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.high, + ), + TaskTemplate( + name: 'Unkraut jaeten', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Hecke schneiden', + intervalType: IntervalType.quarterly, + effortLevel: EffortLevel.high, + ), + TaskTemplate( + name: 'Laub harken', + intervalType: IntervalType.weekly, + effortLevel: EffortLevel.medium, + ), + TaskTemplate( + name: 'Pflanzen giessen', + intervalType: IntervalType.everyNDays, + intervalDays: 2, + effortLevel: EffortLevel.low, + ), + ], +}; + +/// Aliases for room type detection. +const Map> _aliases = { + 'badezimmer': ['bad', 'wc', 'toilette'], + 'buero': ['arbeitszimmer', 'office'], + 'waschkueche': ['waschraum'], + 'garten': ['terrasse', 'aussenbereich', 'hof'], + 'gaestezimmer': ['gaeste'], + 'kinderzimmer': ['kinder'], + 'schlafzimmer': ['schlafraum'], + 'kueche': ['kitchen'], + 'esszimmer': ['essbereich'], +}; + +/// Detect a room type from a user-entered room name. +/// +/// Performs case-insensitive matching against room type keys and their +/// aliases. Returns the room type key (e.g. "kueche") or null if no match. +String? detectRoomType(String roomName) { + final lower = roomName.toLowerCase().trim(); + + // Check direct room type keys first + for (final type in roomTemplates.keys) { + if (lower.contains(type)) { + return type; + } + } + + // Check aliases + for (final entry in _aliases.entries) { + for (final alias in entry.value) { + if (lower.contains(alias)) { + return entry.key; + } + } + } + + return null; +} diff --git a/test/features/templates/task_templates_test.dart b/test/features/templates/task_templates_test.dart new file mode 100644 index 0000000..a0993b1 --- /dev/null +++ b/test/features/templates/task_templates_test.dart @@ -0,0 +1,111 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:household_keeper/features/tasks/domain/effort_level.dart'; +import 'package:household_keeper/features/tasks/domain/frequency.dart'; +import 'package:household_keeper/features/templates/data/task_templates.dart'; + +void main() { + group('roomTemplates', () { + test('contains exactly 14 room type keys', () { + expect(roomTemplates.length, 14); + }); + + test('all 14 room types from TMPL-02 are present', () { + const expectedTypes = [ + 'kueche', + 'badezimmer', + 'schlafzimmer', + 'wohnzimmer', + 'flur', + 'buero', + 'garage', + 'balkon', + 'waschkueche', + 'keller', + 'kinderzimmer', + 'gaestezimmer', + 'esszimmer', + 'garten', + ]; + for (final type in expectedTypes) { + expect( + roomTemplates.containsKey(type), + isTrue, + reason: 'Missing room type: $type', + ); + } + }); + + test('each room type maps to a non-empty list of TaskTemplate objects', () { + for (final entry in roomTemplates.entries) { + expect( + entry.value, + isNotEmpty, + reason: 'Room type ${entry.key} has no templates', + ); + } + }); + + test('every TaskTemplate has a non-empty German name', () { + for (final entry in roomTemplates.entries) { + for (final template in entry.value) { + expect( + template.name.isNotEmpty, + isTrue, + reason: + 'Template in ${entry.key} has empty name', + ); + } + } + }); + + test('every TaskTemplate has a valid IntervalType', () { + for (final entry in roomTemplates.entries) { + for (final template in entry.value) { + expect( + IntervalType.values.contains(template.intervalType), + isTrue, + reason: + 'Template "${template.name}" in ${entry.key} has invalid intervalType', + ); + } + } + }); + + test('every TaskTemplate has a valid EffortLevel', () { + for (final entry in roomTemplates.entries) { + for (final template in entry.value) { + expect( + EffortLevel.values.contains(template.effortLevel), + isTrue, + reason: + 'Template "${template.name}" in ${entry.key} has invalid effortLevel', + ); + } + } + }); + }); + + group('detectRoomType', () { + test('"Kueche" returns "kueche"', () { + expect(detectRoomType('Kueche'), 'kueche'); + }); + + test('"Mein Badezimmer" returns "badezimmer"', () { + expect(detectRoomType('Mein Badezimmer'), 'badezimmer'); + }); + + test('"Bad" returns "badezimmer" (alias)', () { + expect(detectRoomType('Bad'), 'badezimmer'); + }); + + test('"Random Name" returns null', () { + expect(detectRoomType('Random Name'), isNull); + }); + + test('is case-insensitive', () { + expect(detectRoomType('KUECHE'), 'kueche'); + expect(detectRoomType('kueche'), 'kueche'); + expect(detectRoomType('Kueche'), 'kueche'); + }); + }); +}