v0.1.0 — Phase 1: Go Backend + SQLite + Seed Data
- Wails project setup (Go + React-TS) - SQLite schema (allergens, additives, products, week_plans, plan_entries, special_days) - 14 EU allergens (LMIV 1169/2011) - 24 German food additives - 99 products imported from Excel with allergen/additive mappings - Full Wails bindings (CRUD for products, week plans, entries, special days) - OTA updater stub (version check against HTTPS endpoint) - Pure Go SQLite (no CGO) for easy Windows cross-compilation
This commit is contained in:
221
seed.go
Normal file
221
seed.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed products_export.json
|
||||
var productsJSON []byte
|
||||
|
||||
// SeedDatabase fügt Seed-Daten in die Datenbank ein
|
||||
func SeedDatabase() error {
|
||||
// Prüfen ob bereits Seeds vorhanden sind
|
||||
var count int
|
||||
err := db.Get(&count, "SELECT COUNT(*) FROM allergens")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check for existing allergens: %w", err)
|
||||
}
|
||||
|
||||
// Nur seeden wenn noch keine Daten vorhanden sind
|
||||
if count > 0 {
|
||||
return nil // Bereits geseedet
|
||||
}
|
||||
|
||||
// Allergene seeden
|
||||
if err := seedAllergens(); err != nil {
|
||||
return fmt.Errorf("failed to seed allergens: %w", err)
|
||||
}
|
||||
|
||||
// Zusatzstoffe seeden
|
||||
if err := seedAdditives(); err != nil {
|
||||
return fmt.Errorf("failed to seed additives: %w", err)
|
||||
}
|
||||
|
||||
// Produkte seeden
|
||||
if err := seedProducts(); err != nil {
|
||||
return fmt.Errorf("failed to seed products: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// seedAllergens fügt die 14 EU-Allergene ein
|
||||
func seedAllergens() error {
|
||||
allergens := []Allergen{
|
||||
{"a", "glutenhaltiges Getreide (Weizen, Roggen, Gerste, Hafer, Dinkel, Kamut)", "allergen"},
|
||||
{"b", "Krebstiere", "allergen"},
|
||||
{"c", "Eier", "allergen"},
|
||||
{"d", "Fisch", "allergen"},
|
||||
{"e", "Erdnüsse", "allergen"},
|
||||
{"f", "Soja(bohnen)", "allergen"},
|
||||
{"g", "Milch (einschließlich Laktose)", "allergen"},
|
||||
{"h", "Schalenfrüchte (Mandeln, Haselnüsse, Walnüsse, Cashew, Pecan, Paranüsse, Pistazien, Macadamia)", "allergen"},
|
||||
{"i", "Sellerie", "allergen"},
|
||||
{"j", "Senf", "allergen"},
|
||||
{"k", "Sesamsamen", "allergen"},
|
||||
{"l", "Schwefeldioxid und Sulfite (> 10 mg/kg oder mg/l)", "allergen"},
|
||||
{"m", "Lupine", "allergen"},
|
||||
{"n", "Weichtiere", "allergen"},
|
||||
}
|
||||
|
||||
for _, allergen := range allergens {
|
||||
_, err := db.NamedExec(
|
||||
"INSERT OR IGNORE INTO allergens (id, name, category) VALUES (:id, :name, :category)",
|
||||
allergen,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert allergen %s: %w", allergen.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// seedAdditives fügt die deutschen Zusatzstoffe ein
|
||||
func seedAdditives() error {
|
||||
additives := []Additive{
|
||||
{"A", "Antioxidationsmittel"},
|
||||
{"B", "Backtriebmittel"},
|
||||
{"E", "Emulgator"},
|
||||
{"F", "Farbstoff"},
|
||||
{"FM", "Festigungsmittel"},
|
||||
{"FH", "Feuchthaltemittel"},
|
||||
{"FÜ", "Füllstoff"},
|
||||
{"G", "Geliermittel"},
|
||||
{"GV", "Geschmacksverstärker"},
|
||||
{"K", "Konservierungsstoff"},
|
||||
{"M", "Mehlbehandlungsmittel"},
|
||||
{"MS", "Modifizierte Stärke"},
|
||||
{"R", "Rieselhilfe"},
|
||||
{"S", "Säuerungsmittel"},
|
||||
{"SR", "Säureregulator"},
|
||||
{"SV", "Schaumverhüter"},
|
||||
{"SCH", "Schmelzsalz"},
|
||||
{"ST", "Stabilisator"},
|
||||
{"SÜ", "Süßungsmittel"},
|
||||
{"T", "Trägerstoff"},
|
||||
{"TG", "Treibgas"},
|
||||
{"TM", "Trennmittel"},
|
||||
{"Ü", "Überzugsmittel"},
|
||||
{"V", "Verdickungsmittel"},
|
||||
}
|
||||
|
||||
for _, additive := range additives {
|
||||
_, err := db.NamedExec(
|
||||
"INSERT OR IGNORE INTO additives (id, name) VALUES (:id, :name)",
|
||||
additive,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert additive %s: %w", additive.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// seedProducts importiert Produkte aus der JSON-Datei
|
||||
func seedProducts() error {
|
||||
var imports []ProductImport
|
||||
if err := json.Unmarshal(productsJSON, &imports); err != nil {
|
||||
return fmt.Errorf("failed to parse products JSON: %w", err)
|
||||
}
|
||||
|
||||
tx, err := db.Beginx()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to begin transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
for _, product := range imports {
|
||||
// Produkt einfügen
|
||||
result, err := tx.NamedExec(
|
||||
"INSERT OR IGNORE INTO products (name, multiline) VALUES (:name, :multiline)",
|
||||
map[string]interface{}{
|
||||
"name": product.Name,
|
||||
"multiline": product.Multiline,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert product %s: %w", product.Name, err)
|
||||
}
|
||||
|
||||
// Produkt-ID ermitteln
|
||||
var productID int64
|
||||
if productID, err = result.LastInsertId(); err != nil {
|
||||
// Wenn INSERT OR IGNORE nichts eingefügt hat, ID über SELECT holen
|
||||
err = tx.Get(&productID, "SELECT id FROM products WHERE name = ?", product.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get product ID for %s: %w", product.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Allergene und Zusatzstoffe parsen und zuordnen
|
||||
allergenIDs, additiveIDs := parseAllergenData(product.Allergens1, product.Allergens2)
|
||||
|
||||
// Allergene zuordnen
|
||||
for _, allergenID := range allergenIDs {
|
||||
_, err := tx.Exec(
|
||||
"INSERT OR IGNORE INTO product_allergens (product_id, allergen_id) VALUES (?, ?)",
|
||||
productID, allergenID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to link allergen %s to product %s: %w", allergenID, product.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Zusatzstoffe zuordnen
|
||||
for _, additiveID := range additiveIDs {
|
||||
_, err := tx.Exec(
|
||||
"INSERT OR IGNORE INTO product_additives (product_id, additive_id) VALUES (?, ?)",
|
||||
productID, additiveID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to link additive %s to product %s: %w", additiveID, product.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// parseAllergenData parst Allergen- und Zusatzstoff-Daten aus den Import-Feldern
|
||||
func parseAllergenData(allergens1, allergens2 string) ([]string, []string) {
|
||||
// Beide Felder zusammenführen
|
||||
combined := strings.TrimSpace(allergens1 + ", " + allergens2)
|
||||
if combined == ", " {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Regex für Allergene (einzelne Kleinbuchstaben a-n)
|
||||
allergenRegex := regexp.MustCompile(`\b[a-n]\b`)
|
||||
// Regex für Zusatzstoffe (Großbuchstaben und Kürzel)
|
||||
additiveRegex := regexp.MustCompile(`\b[A-Z]{1,3}\b`)
|
||||
|
||||
allergenMatches := allergenRegex.FindAllString(combined, -1)
|
||||
additiveMatches := additiveRegex.FindAllString(combined, -1)
|
||||
|
||||
// Duplikate entfernen
|
||||
allergenIDs := removeDuplicates(allergenMatches)
|
||||
additiveIDs := removeDuplicates(additiveMatches)
|
||||
|
||||
return allergenIDs, additiveIDs
|
||||
}
|
||||
|
||||
// removeDuplicates entfernt Duplikate aus einem String-Slice
|
||||
func removeDuplicates(slice []string) []string {
|
||||
seen := make(map[string]bool)
|
||||
result := []string{}
|
||||
|
||||
for _, item := range slice {
|
||||
if !seen[item] {
|
||||
seen[item] = true
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user