1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
package api
import (
"context"
"fmt"
"time"
"task-dashboard/internal/models"
)
const planToEatBaseURL = "https://www.plantoeat.com/api/v2"
// PlanToEatClient handles interactions with the PlanToEat API
type PlanToEatClient struct {
BaseClient
apiKey string
}
// NewPlanToEatClient creates a new PlanToEat API client
func NewPlanToEatClient(apiKey string) *PlanToEatClient {
return &PlanToEatClient{
BaseClient: NewBaseClient(planToEatBaseURL),
apiKey: apiKey,
}
}
func (c *PlanToEatClient) authHeaders() map[string]string {
return map[string]string{"Authorization": "Bearer " + c.apiKey}
}
// planToEatPlannerItem represents a planner item from the API
type planToEatPlannerItem struct {
ID int `json:"id"`
Date string `json:"date"`
MealType string `json:"meal_type"`
Recipe struct {
ID int `json:"id"`
Title string `json:"title"`
URL string `json:"url"`
} `json:"recipe"`
}
// planToEatResponse wraps the API response
type planToEatResponse struct {
Items []planToEatPlannerItem `json:"items"`
}
// GetUpcomingMeals fetches meals for the next N days
func (c *PlanToEatClient) GetUpcomingMeals(ctx context.Context, days int) ([]models.Meal, error) {
if days <= 0 {
days = 7
}
startDate := time.Now()
endDate := startDate.AddDate(0, 0, days)
path := fmt.Sprintf("/planner_items?start_date=%s&end_date=%s",
startDate.Format("2006-01-02"),
endDate.Format("2006-01-02"))
var apiResponse planToEatResponse
if err := c.Get(ctx, path, c.authHeaders(), &apiResponse); err != nil {
return nil, fmt.Errorf("failed to fetch meals: %w", err)
}
meals := make([]models.Meal, 0, len(apiResponse.Items))
for _, item := range apiResponse.Items {
mealDate, err := time.Parse("2006-01-02", item.Date)
if err != nil {
continue
}
meals = append(meals, models.Meal{
ID: fmt.Sprintf("%d", item.ID),
RecipeName: item.Recipe.Title,
Date: mealDate,
MealType: normalizeMealType(item.MealType),
RecipeURL: item.Recipe.URL,
})
}
return meals, nil
}
// normalizeMealType ensures meal type matches our expected values
func normalizeMealType(mealType string) string {
switch mealType {
case "breakfast", "Breakfast":
return "breakfast"
case "lunch", "Lunch":
return "lunch"
case "dinner", "Dinner":
return "dinner"
case "snack", "Snack":
return "snack"
default:
return "dinner"
}
}
// GetRecipes fetches recipes (for Phase 2)
func (c *PlanToEatClient) GetRecipes(ctx context.Context) error {
return fmt.Errorf("not implemented yet")
}
// AddMealToPlanner adds a meal to the planner (for Phase 2)
func (c *PlanToEatClient) AddMealToPlanner(ctx context.Context, recipeID string, date time.Time, mealType string) error {
return fmt.Errorf("not implemented yet")
}
|