summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2024-04-04 14:54:21 +0200
committerxengineering <me@xengineering.eu>2024-05-12 20:52:25 +0200
commit87ae71413e47ef34da57bc1e0b8dddbf84b0c66a (patch)
treef8711576d7098d21e48eafd9df3ab6f3ed679b88
parentfec2fea87615e6ee3a6e73f7f98e021eee1b7098 (diff)
downloadceres-87ae71413e47ef34da57bc1e0b8dddbf84b0c66a.tar
ceres-87ae71413e47ef34da57bc1e0b8dddbf84b0c66a.tar.zst
ceres-87ae71413e47ef34da57bc1e0b8dddbf84b0c66a.zip
model: Add per-step ingredients
-rw-r--r--model/ingredient.go121
-rw-r--r--model/recipe.go13
-rw-r--r--model/sql/migrate.sql10
-rw-r--r--model/step.go101
4 files changed, 236 insertions, 9 deletions
diff --git a/model/ingredient.go b/model/ingredient.go
new file mode 100644
index 0000000..85a4729
--- /dev/null
+++ b/model/ingredient.go
@@ -0,0 +1,121 @@
+package model
+
+import (
+ "database/sql"
+ "errors"
+ "fmt"
+)
+
+type Ingredient struct {
+ Id string `json:"id"`
+ Index string `json:"index"`
+ Amount string `json:"amount"`
+ Unit string `json:"unit"`
+ Type string `json:"type"`
+ Step string `json:"step"`
+}
+
+func (i *Ingredient) Create(tx *sql.Tx) error {
+ if i.Id != "" {
+ return fmt.Errorf("Cannot create ingredient if ID is given")
+ }
+
+ cmd := `
+INSERT INTO ingredients
+ ('index', amount, unit, 'type', step)
+VALUES
+ (?, ?, ?, ?, ?)
+`
+
+ result, err := tx.Exec(cmd, i.Index, i.Amount, i.Unit, i.Type, i.Step)
+ if err != nil {
+ return err
+ }
+
+ id, err := result.LastInsertId()
+ if err != nil {
+ return err
+ }
+
+ i.Id = fmt.Sprint(id)
+
+ return nil
+}
+
+func (i *Ingredient) Read(tx *sql.Tx) error {
+ cmd := `
+SELECT
+ "index", amount, unit, "type", step
+FROM
+ ingredients
+WHERE
+ id = ?
+`
+
+ rows, err := tx.Query(cmd, i.Id)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ return sql.ErrNoRows
+ }
+
+ err = rows.Scan(&i.Index, &i.Amount, &i.Unit, &i.Type, &i.Step)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (i *Ingredient) Update(tx *sql.Tx) error {
+ cmd := `
+UPDATE
+ ingredients
+SET
+ index = ?,
+ amount = ?,
+ unit = ?,
+ type = ?,
+ step = ?
+WHERE
+ id = ?`
+
+ res, err := tx.Exec(cmd, i.Index, i.Amount, i.Unit, i.Type, i.Step, i.Id)
+ if err != nil {
+ return err
+ }
+
+ affected, err := res.RowsAffected()
+ if err != nil {
+ return err
+ }
+ if affected != 1 {
+ return fmt.Errorf("Ingredient update affected %d rows instead of 1", affected)
+ }
+
+ return nil
+}
+
+func (i *Ingredient) Delete(tx *sql.Tx) error {
+ cmd := `
+DELETE FROM
+ ingredients
+WHERE
+ id = ?
+`
+
+ result, err := tx.Exec(cmd, i.Id)
+ if err != nil {
+ return err
+ }
+
+ rows, err := result.RowsAffected()
+ if rows != 1 {
+ return errors.New("Ingredient deletion did not affect exactly one row")
+ }
+
+ return nil
+}
diff --git a/model/recipe.go b/model/recipe.go
index d8c0f2f..59a4c4a 100644
--- a/model/recipe.go
+++ b/model/recipe.go
@@ -254,9 +254,16 @@ func RecipeTestData() []Recipe {
Created: "",
LastChanged: "",
Steps: []Step{
- {Text: "Stir the dough"},
- {Text: "Heat up pan"},
- {Text: "Make pancakes!"},
+ {
+ Text: "Stir the dough",
+ Ingredients: []Ingredient{
+ {Amount: "4", Unit: "pieces", Type: "egg"},
+ {Amount: "800", Unit: "g", Type: "special stuff"},
+ {Amount: "0.5", Unit: "l", Type: "milk"},
+ },
+ },
+ {Text: "Heat up pan", Ingredients: []Ingredient{}},
+ {Text: "Make pancakes!", Ingredients: []Ingredient{}},
},
},
{
diff --git a/model/sql/migrate.sql b/model/sql/migrate.sql
index f931af6..46f9fc6 100644
--- a/model/sql/migrate.sql
+++ b/model/sql/migrate.sql
@@ -17,3 +17,13 @@ CREATE TABLE steps (
recipe INTEGER NOT NULL,
FOREIGN KEY(recipe) REFERENCES recipes(id)
);
+
+CREATE TABLE ingredients (
+ id INTEGER PRIMARY KEY,
+ 'index' INTEGER NOT NULL,
+ amount REAL NOT NULL,
+ unit TEXT NOT NULL,
+ 'type' TEXT NOT NULL,
+ step INTEGER NOT NULL,
+ FOREIGN KEY(step) REFERENCES steps(id)
+);
diff --git a/model/step.go b/model/step.go
index f689fb9..74b4f48 100644
--- a/model/step.go
+++ b/model/step.go
@@ -8,10 +8,11 @@ import (
)
type Step struct {
- Id string `json:"id"`
- Index string `json:"index"`
- Text string `json:"text"`
- Recipe string `json:"recipe"`
+ Id string `json:"id"`
+ Index string `json:"index"`
+ Text string `json:"text"`
+ Recipe string `json:"recipe"`
+ Ingredients []Ingredient `json:"ingredients"`
}
func (s Step) String() string {
@@ -43,9 +44,52 @@ VALUES
s.Id = fmt.Sprint(id)
+ for i := range s.Ingredients {
+ s.Ingredients[i].Step = s.Id
+ s.Ingredients[i].Index = fmt.Sprint(i)
+ err = s.Ingredients[i].Create(tx)
+ if err != nil {
+ return err
+ }
+ }
+
return nil
}
+func (s *Step) getIngredientIds(tx *sql.Tx) ([]Ingredient, error) {
+ retval := make([]Ingredient, 0)
+
+ cmd := `
+SELECT
+ id
+FROM
+ ingredients
+WHERE
+ step = ?
+ORDER BY
+ 'index' ASC
+`
+
+ rows, err := tx.Query(cmd, s.Id)
+ if err != nil {
+ return retval, err
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ i := Ingredient{}
+
+ err = rows.Scan(&i.Id)
+ if err != nil {
+ return retval, err
+ }
+
+ retval = append(retval, i)
+ }
+
+ return retval, nil
+}
+
func (s *Step) Read(tx *sql.Tx) error {
cmd := `
SELECT
@@ -71,10 +115,43 @@ WHERE
return err
}
+ s.Ingredients, err = s.getIngredientIds(tx)
+ if err != nil {
+ return err
+ }
+
+ for i := range s.Ingredients {
+ err = s.Ingredients[i].Read(tx)
+ if err != nil {
+ return err
+ }
+ }
+
return nil
}
func (s *Step) Update(tx *sql.Tx) error {
+ oldIngredients, err := s.getIngredientIds(tx)
+ if err != nil {
+ return err
+ }
+
+ for i := range oldIngredients {
+ err = oldIngredients[i].Delete(tx)
+ if err != nil {
+ return err
+ }
+ }
+
+ for i := range s.Ingredients {
+ s.Ingredients[i].Index = fmt.Sprint(i)
+ s.Ingredients[i].Step = s.Id
+ s.Ingredients[i].Create(tx)
+ if err != nil {
+ return err
+ }
+ }
+
cmd := `
UPDATE
steps
@@ -95,13 +172,25 @@ WHERE
return err
}
if affected != 1 {
- return fmt.Errorf("Recipe update affected %d rows instead of 1", affected)
+ return fmt.Errorf("Step update affected %d rows instead of 1", affected)
}
return nil
}
func (s *Step) Delete(tx *sql.Tx) error {
+ oldIngredients, err := s.getIngredientIds(tx)
+ if err != nil {
+ return err
+ }
+
+ for i := range oldIngredients {
+ err = oldIngredients[i].Delete(tx)
+ if err != nil {
+ return err
+ }
+ }
+
cmd := `
DELETE FROM
steps
@@ -116,7 +205,7 @@ WHERE
rows, err := result.RowsAffected()
if rows != 1 {
- return errors.New("Recipe deletion did not affect exactly one row")
+ return errors.New("Step deletion did not affect exactly one row")
}
return nil