diff --git a/foodservice.go b/foodservice.go index 4476e65..64ea79e 100644 --- a/foodservice.go +++ b/foodservice.go @@ -34,21 +34,20 @@ type ( const foodColumns = "id, date, food, description, amount, per100, energy" const foodAggregatedColumns = "period, amount, avgPer100, energy" -func (s *FoodService) GetRecent() ([]Food, error) { - var res []Food = []Food{} +func (s *FoodService) GetRecent() (res []Food, err error) { + log.Printf("Getting recent food") if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get recent food, db is nil or is not ready") } row, err := s.db.readConn.Query(fmt.Sprintf("SELECT %s from foodView WHERE date > datetime('now', '-%d days') order by date desc;", foodColumns, Settings.FoodDaysLookback)) if err != nil { - log.Printf("error getting daily food: %v", err) - return res, err + return res, fmt.Errorf("error getting recent food: %w", err) } for row.Next() { var food Food - err := row.Scan(&food.Id, &food.Date, &food.Food, &food.Descripton, &food.Amount, &food.Per100, &food.Energy) + err = row.Scan(&food.Id, &food.Date, &food.Food, &food.Descripton, &food.Amount, &food.Per100, &food.Energy) if err != nil { log.Printf("error scanning row: %v", err) continue @@ -57,11 +56,12 @@ func (s *FoodService) GetRecent() ([]Food, error) { res = append(res, food) } - return res, nil + log.Printf("Got %d recent foods", len(res)) + return } -func (s *FoodService) GetLastPer100(name string) ([]FoodSearch, error) { - res := []FoodSearch{} +func (s *FoodService) GetLastPer100(name string) (res []FoodSearch, err error) { + log.Printf("Getting last per100 for %s", name) if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get last per100, db is nil or is not ready") } @@ -83,13 +83,12 @@ limit %d // log.Printf("%#v", query) rows, err := s.db.readConn.Query(query, name) if err != nil { - log.Printf("error getting last per100: %v", err) - return res, err + return res, fmt.Errorf("error getting last per100: %w", err) } for rows.Next() { var f FoodSearch - err := rows.Scan(&f.Id, &f.Date, &f.Food.Food, &f.Descripton, &f.Amount, &f.Per100, &f.Energy, &f.Score) + err = rows.Scan(&f.Id, &f.Date, &f.Food.Food, &f.Descripton, &f.Amount, &f.Per100, &f.Energy, &f.Score) if err != nil { log.Printf("error scanning row: %v", err) continue @@ -101,10 +100,12 @@ limit %d return nil, fmt.Errorf("no results found for %s", name) } + log.Printf("Got %d last per100 foods for %s", len(res), name) return res, nil } -func (s *FoodService) Create(food Food) (Food, error) { +func (s *FoodService) Create(food Food) (res Food, err error) { + log.Printf("Creating food %v", food) if s.db == nil || !s.db.Ready { return food, fmt.Errorf("cannot create food, db is nil or is not ready") } @@ -115,29 +116,30 @@ func (s *FoodService) Create(food Food) (Food, error) { return food, fmt.Errorf("cannot create food, amount is less than or equal to 0") } - var res sql.Result - var err error + var dbres sql.Result if food.Per100 > 0 { - res, err = s.db.writeConn.Exec("INSERT INTO food (food, description, amount, per100) VALUES (?, ?, ?, ?)", food.Food, food.Descripton, food.Amount, food.Per100) + dbres, err = s.db.writeConn.Exec("INSERT INTO food (food, description, amount, per100) VALUES (?, ?, ?, ?)", food.Food, food.Descripton, food.Amount, food.Per100) if err != nil { - return food, fmt.Errorf("error inserting food: %v", err) + return food, fmt.Errorf("error inserting food: %w", err) } } else { - res, err = s.db.writeConn.Exec("INSERT INTO food (food, description, amount) VALUES (?, ?, ?)", food.Food, food.Descripton, food.Amount) + dbres, err = s.db.writeConn.Exec("INSERT INTO food (food, description, amount) VALUES (?, ?, ?)", food.Food, food.Descripton, food.Amount) if err != nil { - return food, fmt.Errorf("error inserting food: %v", err) + return food, fmt.Errorf("error inserting food: %w", err) } } - id, err := res.LastInsertId() + id, err := dbres.LastInsertId() if err != nil { - return food, fmt.Errorf("error getting last insert id: %v", err) + return food, fmt.Errorf("error getting last insert id: %w", err) } + log.Printf("Created food %s with id %d", food.Food, id) return s.GetById(id) } -func (s *FoodService) Update(food Food) (Food, error) { +func (s *FoodService) Update(food Food) (res Food, err error) { + log.Printf("Updating food %s with id %d", food.Food, food.Id) if s.db == nil || !s.db.Ready { return food, fmt.Errorf("cannot update food, db is nil or is not ready") } @@ -154,45 +156,46 @@ func (s *FoodService) Update(food Food) (Food, error) { if food.Per100 > 0 { _, err := s.db.writeConn.Exec("UPDATE food SET food = ?, description = ?, amount = ?, per100 = ? WHERE Id = ?", food.Food, food.Descripton, food.Amount, food.Per100, food.Id) if err != nil { - return food, fmt.Errorf("error updating food: %v", err) + return food, fmt.Errorf("error updating food: %w", err) } } else { _, err := s.db.writeConn.Exec("UPDATE food SET food = ?, description = ?, amount = ? WHERE Id = ?", food.Food, food.Descripton, food.Amount, food.Id) if err != nil { - return food, fmt.Errorf("error updating food: %v", err) + return food, fmt.Errorf("error updating food: %w", err) } } + log.Printf("Updated food %s with id %d", food.Food, food.Id) return s.GetById(food.Id) } -func (s *FoodService) GetById(id int64) (Food, error) { - var res Food +func (s *FoodService) GetById(id int64) (res Food, err error) { + log.Printf("Getting food by id %d", id) if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get food by id, db is nil or is not ready") } row := s.db.readConn.QueryRow(fmt.Sprintf("SELECT %s from foodView WHERE id = ?", foodColumns), id) - err := row.Scan(&res.Id, &res.Date, &res.Food, &res.Descripton, &res.Amount, &res.Per100, &res.Energy) + err = row.Scan(&res.Id, &res.Date, &res.Food, &res.Descripton, &res.Amount, &res.Per100, &res.Energy) if err != nil { - return res, fmt.Errorf("error scanning row: %v", err) + return res, fmt.Errorf("error scanning row: %w", err) } + log.Printf("Got food %s with id %d", res.Food, res.Id) return res, nil } // I could probably refactor this to be less of a disaster... // But I think it'll work for now -func (s *FoodService) GetDaily() ([]AggregatedFood, error) { - res := []AggregatedFood{} +func (s *FoodService) GetDaily() (res []AggregatedFood, err error) { + log.Printf("Getting daily food") if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get daily food, db is nil or is not ready") } row, err := s.db.readConn.Query(fmt.Sprintf("SELECT %s from foodDaily LIMIT %d", foodAggregatedColumns, Settings.FoodDailyLookback)) if err != nil { - log.Printf("error getting daily food: %v", err) - return res, err + return res, fmt.Errorf("error getting daily food: %w", err) } for row.Next() { @@ -206,24 +209,24 @@ func (s *FoodService) GetDaily() ([]AggregatedFood, error) { res = append(res, food) } + log.Printf("Got %d daily foods", len(res)) return res, nil } -func (s *FoodService) GetWeekly() ([]AggregatedFood, error) { - res := []AggregatedFood{} +func (s *FoodService) GetWeekly() (res []AggregatedFood, err error) { + log.Printf("Getting weekly food") if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get weekly food, db is nil or is not ready") } row, err := s.db.readConn.Query(fmt.Sprintf("SELECT %s from foodWeekly LIMIT %d", foodAggregatedColumns, Settings.FoodWeeklyLookback)) if err != nil { - log.Printf("error getting weekly food: %v", err) - return res, err + return res, fmt.Errorf("error getting weekly food: %w", err) } for row.Next() { var food AggregatedFood - err := row.Scan(&food.Period, &food.Amount, &food.AvgPer100, &food.Energy) + err = row.Scan(&food.Period, &food.Amount, &food.AvgPer100, &food.Energy) if err != nil { log.Printf("error scanning row: %v", err) continue @@ -232,24 +235,24 @@ func (s *FoodService) GetWeekly() ([]AggregatedFood, error) { res = append(res, food) } + log.Printf("Got %d weekly foods", len(res)) return res, nil } -func (s *FoodService) GetMonthly() ([]AggregatedFood, error) { - res := []AggregatedFood{} +func (s *FoodService) GetMonthly() (res []AggregatedFood, err error) { + log.Printf("Getting monthly food") if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get monthly food, db is nil or is not ready") } row, err := s.db.readConn.Query(fmt.Sprintf("SELECT %s from foodMonthly LIMIT %d", foodAggregatedColumns, Settings.FoodMonthlyLookback)) if err != nil { - log.Printf("error getting monthly food: %v", err) - return res, err + return res, fmt.Errorf("error getting monthly food: %w", err) } for row.Next() { var food AggregatedFood - err := row.Scan(&food.Period, &food.Amount, &food.AvgPer100, &food.Energy) + err = row.Scan(&food.Period, &food.Amount, &food.AvgPer100, &food.Energy) if err != nil { log.Printf("error scanning row: %v", err) continue @@ -258,24 +261,24 @@ func (s *FoodService) GetMonthly() ([]AggregatedFood, error) { res = append(res, food) } + log.Printf("Got %d monthly foods", len(res)) return res, nil } -func (s *FoodService) GetYearly() ([]AggregatedFood, error) { - res := []AggregatedFood{} +func (s *FoodService) GetYearly() (res []AggregatedFood, err error) { + log.Printf("Getting yearly food") if s.db == nil || !s.db.Ready { return res, fmt.Errorf("cannot get yearly food, db is nil or is not ready") } row, err := s.db.readConn.Query(fmt.Sprintf("SELECT %s from foodYearly LIMIT %d", foodAggregatedColumns, Settings.FoodYearlyLookback)) if err != nil { - log.Printf("error getting yearly food: %v", err) - return res, err + return res, fmt.Errorf("error getting yearly food: %w", err) } for row.Next() { var food AggregatedFood - err := row.Scan(&food.Period, &food.Amount, &food.AvgPer100, &food.Energy) + err = row.Scan(&food.Period, &food.Amount, &food.AvgPer100, &food.Energy) if err != nil { log.Printf("error scanning row: %v", err) continue @@ -284,5 +287,6 @@ func (s *FoodService) GetYearly() ([]AggregatedFood, error) { res = append(res, food) } + log.Printf("Got %d yearly foods", len(res)) return res, nil } diff --git a/migratev2.sql b/migratev2.sql new file mode 100644 index 0000000..c62c3c8 --- /dev/null +++ b/migratev2.sql @@ -0,0 +1,189 @@ +drop index weightDateIdx; +drop index weightDailyIdx; +drop index weightWeeklyIdx; +drop index weightMonthlyIdx; +drop index weightYearlyIdx; + +create table weight2 ( + id integer primary key, + date datetime default (datetime('now')), + weight real not null +); +insert into weight2 select * from weight; +drop table weight; + +drop view if exists weightView; +drop view if exists weightDaily; +drop view if exists weightWeekly; +drop view if exists weightMonthly; +drop view if exists weightYearly; + +alter table weight2 rename to weight; + +create index weightDateIdx on weight(date); +create index weightDailyIdx on weight(strftime('%Y-%m-%d', date)); +create index weightWeeklyIdx on weight(strftime('%Y-%W', date)); +create index weightMonthlyIdx on weight(strftime('%Y-%m', date)); +create index weightYearlyIdx on weight(strftime('%Y', date)); + +create view weightDaily as +select strftime('%Y-%m-%d', date) as period, + round(avg(weight), 2) as amount +from weight +group by strftime('%Y-%m-%d', date) +order by date desc; + +create view weightWeekly as +select strftime('%Y-%W', date) as period, + round(avg(weight), 2) as amount +from weight +group by strftime('%Y-%W', date) +order by date desc; + +create view weightMonthly as +select strftime('%Y-%m', date) as period, + round(avg(weight), 2) as amount +from weight +group by strftime('%Y-%m', date) +order by date desc; + +create view weightYearly as +select strftime('%Y', date) as period, + round(avg(weight), 2) as amount +from weight +group by strftime('%Y', date) +order by date desc; + + +create table food2 ( + id integer primary key, + date datetime default (datetime('now')), + food varchar not null, + description varchar, + amount real not null, + per100 real default 0, + energy generated always as (coalesce(amount, 0) * coalesce(per100, 0) / 100) stored +); + +drop index dailyIdx; +drop index weeklyIdx; +drop index monthlyIdx; +drop index yearlyIdx; +drop index dateIdx; +drop index foodIdx; + +drop view if exists foodView; +drop view if exists foodDaily; +drop view if exists foodWeekly; +drop view if exists foodMonthly; +drop view if exists foodYearly; +drop view if exists foodRecent; + +insert into food2(date, food, description, amount) select date, food, description, amount from food; +drop table food; +alter table food2 rename to food; + +create index dailyIdx on food(strftime('%Y-%m-%d', date)); +create index weeklyIdx on food(strftime('%Y-%W', date)); +create index monthlyIdx on food(strftime('%Y-%m', date)); +create index yearlyIdx on food(strftime('%Y', date)); +create index dateIdx on food(date); +create index foodIdx on food(food); + +create view foodView as +select id, + date, + food, + description, + round(amount, 2) as amount, + round(per100, 2) as per100, + round(energy, 2) as energy +from food; + +create view foodDaily as +select strftime('%Y-%m-%d', date) as period, + round(sum(amount), 2) as amount, + round(avg(per100), 2) as avgPer100, + round(sum(energy), 2) as energy +from food +group by strftime('%Y-%m-%d', date) +order by date desc; + +create view foodWeekly as +select strftime('%Y-%W', date) as period, + round(sum(amount), 2) as amount, + round(avg(per100), 2) as avgPer100, + round(sum(energy), 2) as energy +from food +group by strftime('%Y-%W', date) +order by date desc; + +create view foodMonthly as +select strftime('%Y-%m', date) as period, + round(sum(amount), 2) as amount, + round(avg(per100), 2) as avgPer100, + round(sum(energy), 2) as energy +from food +group by strftime('%Y-%m', date) +order by date desc; + +create view foodYearly as +select strftime('%Y', date) as period, + round(sum(amount), 2) as amount, + round(avg(per100), 2) as avgPer100, + round(sum(energy), 2) as energy +from food +group by strftime('%Y', date) +order by date desc; + +create view foodRecent as +select * +from food +order by date desc +limit 10; + +drop trigger if exists food_foodfix_insert; +create trigger food_foodfix_insert AFTER +insert on food for EACH row +begin +update foodfix +set rank = rank + 1 +where word = new.food; +insert into foodfix (word, rank) +select new.food, + 1 +where not exists ( + select 1 + from foodfix + where word = new.food + ); +end; + +drop trigger if exists food_foodfix_delete; +create trigger food_foodfix_delete AFTER +delete on food for EACH row +begin +update foodfix +set rank = rank - 1 +where word = old.food; + +delete from foodfix +where word = old.food + and rank <= 0; +end; + +drop trigger if exists food_foodfix_update; +create trigger food_foodfix_update AFTER +update on food for EACH row +begin +update foodfix +set rank = rank - 1 +where word = old.food; +end; + +delete from foodfix; +insert into foodfix (word, rank) +select food as word, + count(*) as rank +from food +group by food; \ No newline at end of file