- 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
221 lines
6.0 KiB
Go
221 lines
6.0 KiB
Go
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
|
|
} |