Smart onboarding: residence home profile + suggestion engine
14 new optional residence fields (heating, cooling, water heater, roof, pool, sprinkler, septic, fireplace, garage, basement, attic, exterior, flooring, landscaping) with JSONB conditions on templates. Suggestion engine scores templates against home profile: string match +0.25, bool +0.3, property type +0.15, universal base 0.3. Graceful degradation from minimal to full profile info. GET /api/tasks/suggestions/?residence_id=X returns ranked templates. 54 template conditions across 44 templates in seed data. 8 suggestion service tests.
This commit is contained in:
@@ -46,6 +46,22 @@ type ResidenceResponse struct {
|
||||
Description string `json:"description"`
|
||||
PurchaseDate *time.Time `json:"purchase_date"`
|
||||
PurchasePrice *decimal.Decimal `json:"purchase_price"`
|
||||
// Home Profile
|
||||
HeatingType *string `json:"heating_type"`
|
||||
CoolingType *string `json:"cooling_type"`
|
||||
WaterHeaterType *string `json:"water_heater_type"`
|
||||
RoofType *string `json:"roof_type"`
|
||||
HasPool bool `json:"has_pool"`
|
||||
HasSprinklerSystem bool `json:"has_sprinkler_system"`
|
||||
HasSeptic bool `json:"has_septic"`
|
||||
HasFireplace bool `json:"has_fireplace"`
|
||||
HasGarage bool `json:"has_garage"`
|
||||
HasBasement bool `json:"has_basement"`
|
||||
HasAttic bool `json:"has_attic"`
|
||||
ExteriorType *string `json:"exterior_type"`
|
||||
FlooringPrimary *string `json:"flooring_primary"`
|
||||
LandscapingType *string `json:"landscaping_type"`
|
||||
|
||||
IsPrimary bool `json:"is_primary"`
|
||||
IsActive bool `json:"is_active"`
|
||||
OverdueCount int `json:"overdue_count"`
|
||||
@@ -184,9 +200,23 @@ func NewResidenceResponse(residence *models.Residence) ResidenceResponse {
|
||||
YearBuilt: residence.YearBuilt,
|
||||
Description: residence.Description,
|
||||
PurchaseDate: residence.PurchaseDate,
|
||||
PurchasePrice: residence.PurchasePrice,
|
||||
IsPrimary: residence.IsPrimary,
|
||||
IsActive: residence.IsActive,
|
||||
PurchasePrice: residence.PurchasePrice,
|
||||
HeatingType: residence.HeatingType,
|
||||
CoolingType: residence.CoolingType,
|
||||
WaterHeaterType: residence.WaterHeaterType,
|
||||
RoofType: residence.RoofType,
|
||||
HasPool: residence.HasPool,
|
||||
HasSprinklerSystem: residence.HasSprinklerSystem,
|
||||
HasSeptic: residence.HasSeptic,
|
||||
HasFireplace: residence.HasFireplace,
|
||||
HasGarage: residence.HasGarage,
|
||||
HasBasement: residence.HasBasement,
|
||||
HasAttic: residence.HasAttic,
|
||||
ExteriorType: residence.ExteriorType,
|
||||
FlooringPrimary: residence.FlooringPrimary,
|
||||
LandscapingType: residence.LandscapingType,
|
||||
IsPrimary: residence.IsPrimary,
|
||||
IsActive: residence.IsActive,
|
||||
CreatedAt: residence.CreatedAt,
|
||||
UpdatedAt: residence.UpdatedAt,
|
||||
}
|
||||
|
||||
15
internal/dto/responses/suggestion.go
Normal file
15
internal/dto/responses/suggestion.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package responses
|
||||
|
||||
// TaskSuggestionResponse represents a single task suggestion with relevance scoring
|
||||
type TaskSuggestionResponse struct {
|
||||
Template TaskTemplateResponse `json:"template"`
|
||||
RelevanceScore float64 `json:"relevance_score"`
|
||||
MatchReasons []string `json:"match_reasons"`
|
||||
}
|
||||
|
||||
// TaskSuggestionsResponse represents the full suggestions response
|
||||
type TaskSuggestionsResponse struct {
|
||||
Suggestions []TaskSuggestionResponse `json:"suggestions"`
|
||||
TotalCount int `json:"total_count"`
|
||||
ProfileCompleteness float64 `json:"profile_completeness"`
|
||||
}
|
||||
Reference in New Issue
Block a user