diff --git a/food.ddl b/food.ddl new file mode 100644 index 0000000..3a9be29 --- /dev/null +++ b/food.ddl @@ -0,0 +1,187 @@ +begin transaction; + +create table weight ( + date datetime default (datetime('now', '+2 hours')), + weight real not null +); + +-- begin TRANSACTION; +-- drop table if exists weight2; +-- create table weight2 ( +-- date datetime default (datetime('now', '+2 hours')), +-- weight real not null +-- ); +-- insert into weight2(date, weight) +-- select date, weight from weight; +-- drop table weight; +-- alter table weight2 rename to weight; +-- commit; + +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)); + +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; + +create view weightView as +select rowid, + date, + round(weight, 2) as weight +from weight +order by date desc; + +-- Daily Summary View +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; + +-- Weekly Summary View +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; + +-- Monthly Summary View +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; + +-- Yearly Summary View +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 food( + date datetime default (datetime('now', '+2 hours')), + 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 +); + +-- begin transaction; +-- drop table if exists food2; +-- create table food2( +-- date datetime default (datetime('now', '+2 hours')), +-- 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 +-- ); +-- insert into food2(date, food, description, amount, per100) +-- select date, food, description, amount, per100 from food; +-- drop table food; +-- alter table food2 rename to food; +-- commit; +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); + +drop view if exists foodView; +drop view if exists daily; +drop view if exists weekly; +drop view if exists monthly; +drop view if exists yearly; +drop view if exists recent; + +create view foodView as +select rowid, + date, + food, + description, + round(amount, 2) as amount, + round(per100, 2) as per100, + round(energy, 2) as energy +from food; + +-- Daily Summary View +create view daily 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; + +-- Weekly Summary View +create view weekly 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; + +-- Monthly Summary View +create view monthly 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; + +-- Yearly Summary View +create view yearly 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; + +-- Recent +create view recent as +select rowid, + * +from food +order by date desc +limit 10; + +drop trigger if exists getper100; +create trigger getper100 after +insert on food +begin +update food +set per100 = coalesce( + new .per100, + ( + select per100 + from food + where food = new .food + and per100 is not null + order by date desc + limit 1 + ) + ) +where rowid = new .rowid; +end; + +create table settings (key text, value text) --rollback; +commit; \ No newline at end of file diff --git a/foodservice.go b/foodservice.go index 815734c..ffca2b1 100644 --- a/foodservice.go +++ b/foodservice.go @@ -1,5 +1,104 @@ package main -type FoodService struct { - db *DB +import ( + "database/sql" + "fmt" + "log" +) + +type ( + FoodService struct { + db *DB + } + Food struct { + rowid int + date string + food string + descripton string + amount float32 + per100 float32 + energy float32 + } + AggregatedFood struct { + period string + amount float32 + avgPer100 float32 + energy float32 + } +) + +const foodColumns = "rowid, date, food, description, amount, per100, energy" +const foodAggregatedColumns = "period, amount, avgPer100, energy" + +func (s *FoodService) GetRecent() ([]Food, error) { + var res []Food + if s.db == nil || !s.db.Ready { + return res, fmt.Errorf("cannot get recent food, db is nil or is not ready") + } + + row, _ := s.db.readConn.Query(fmt.Sprintf("SELECT %s from foodView WHERE date > datetime('now', '-%s days')", foodColumns, Settings.FoodDaysLookback)) + + var rows []Food + for row.Next() { + var food Food + err := row.Scan(&food.rowid, &food.date, &food.food, &food.descripton, &food.amount, &food.per100, &food.energy) + if err != nil { + log.Printf("error scanning row: %v", err) + continue + } + + rows = append(rows, food) + } + + log.Printf("%++v", rows) + + return res, nil +} + +func (s *FoodService) Create(food Food) (Food, error) { + if s.db == nil || !s.db.Ready { + return food, fmt.Errorf("cannot create food, db is nil or is not ready") + } + if food.food == "" { + return food, fmt.Errorf("cannot create food, food is empty") + } + if food.amount <= 0 { + return food, fmt.Errorf("cannot create food, amount is less than or equal to 0") + } + + var res sql.Result + var err error + 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) + if err != nil { + return food, fmt.Errorf("error inserting food: %v", err) + } + } else { + res, 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) + } + } + + rowid, err := res.LastInsertId() + if err != nil { + return food, fmt.Errorf("error getting last insert id: %v", err) + } + + return s.GetByRowid(rowid) +} + +func (s *FoodService) GetByRowid(rowid int64) (Food, error) { + var res Food + if s.db == nil || !s.db.Ready { + return res, fmt.Errorf("cannot get food by rowid, db is nil or is not ready") + } + + row := s.db.readConn.QueryRow(fmt.Sprintf("SELECT %s from foodView WHERE rowid = ?", foodColumns), rowid) + err := row.Scan(&res.rowid, &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, nil } \ No newline at end of file diff --git a/main.go b/main.go index 00784d2..72b9f0c 100644 --- a/main.go +++ b/main.go @@ -53,7 +53,22 @@ func main() { Error.Printf("%++v", err) os.Exit(1) } - log.Println(Settings) + log.Printf("Loaded settings as: %++v", Settings) + + foodService := FoodService{db: &db} + + // _, err = foodService.GetRecent() + // if err != nil { + // Error.Printf("%++v", err) + // os.Exit(1) + // } + + food, err := foodService.Create(Food{food: "test", amount: 1}) + if err != nil { + Error.Printf("%++v", err) + os.Exit(1) + } + log.Println(food) log.Println("done") wg.Wait() diff --git a/types.go b/types.go index 06aba48..59608a5 100644 --- a/types.go +++ b/types.go @@ -3,21 +3,7 @@ package main import "time" type ( - Food struct { - rowid int - date time.Time - food string - descripton string - amount float32 - per100 float32 - energy float32 - } - AggregatedFood struct { - period string - amount float32 - avgPer100 float32 - energy float32 - } + Weight struct { rowid int date time.Time