Implement basic food service
This commit is contained in:
187
food.ddl
Normal file
187
food.ddl
Normal file
@@ -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;
|
101
foodservice.go
101
foodservice.go
@@ -1,5 +1,104 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
type FoodService struct {
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
FoodService struct {
|
||||||
db *DB
|
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
|
||||||
}
|
}
|
17
main.go
17
main.go
@@ -53,7 +53,22 @@ func main() {
|
|||||||
Error.Printf("%++v", err)
|
Error.Printf("%++v", err)
|
||||||
os.Exit(1)
|
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")
|
log.Println("done")
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
16
types.go
16
types.go
@@ -3,21 +3,7 @@ package main
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type (
|
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 {
|
Weight struct {
|
||||||
rowid int
|
rowid int
|
||||||
date time.Time
|
date time.Time
|
||||||
|
Reference in New Issue
Block a user