Files
descrybe/apps/api/internal/eprel/attrs.go
T

146 lines
3.4 KiB
Go
Raw Normal View History

2026-08-16 16:57:36 +02:00
package eprel
import (
"fmt"
"strings"
)
// ExtractFromAttrs builds the nested EPREL object used by V1 process items and
// the dashboard product API (id / label / pdf / energy_class / energy_scale).
// Returns nil when no usable EPREL fields are present.
//
// Nested attrs["eprel"] may use flat export aliases (eprel_id, eprel_label, …);
// those are normalized onto the public keys. Flat eprel_* keys fill any gaps.
func ExtractFromAttrs(attrs map[string]any) any {
if attrs == nil {
return nil
}
out := map[string]any{}
if e, ok := attrs["eprel"]; ok && e != nil {
switch t := e.(type) {
case map[string]any:
for k, v := range t {
if s := attrString(v); s == "" {
continue
} else {
out[strings.TrimSpace(k)] = s
}
}
default:
if s := attrString(e); s != "" {
return map[string]any{"id": s}
}
return nil
}
}
put := func(dstKey string, keys ...string) {
if attrString(out[dstKey]) != "" {
return
}
for _, k := range keys {
if s := attrString(attrs[k]); s != "" {
out[dstKey] = s
return
}
// Also accept aliases already copied from a nested object.
if s := attrString(out[k]); s != "" {
out[dstKey] = s
return
}
}
}
put("id", "eprel_id", "EPRELID", "eprelId")
put("label", "eprel_label", "eprel_label_url")
put("pdf", "eprel_pdf", "eprel_pdf_url")
put("energy_class", "eprel_energy_class", "energyClass")
put("energy_scale", "eprel_energy_scale", "energyClassRange", "energyScale")
return NormalizeShape(out)
}
// NormalizeShape keeps only the public EPREL keys with non-empty string values.
// Alias keys (eprel_id, …) are folded onto id/label/pdf/energy_class/energy_scale.
func NormalizeShape(v any) any {
if v == nil {
return nil
}
m, ok := v.(map[string]any)
if !ok {
return nil
}
if len(m) == 0 {
return nil
}
out := map[string]any{}
for k, val := range m {
if s := attrString(val); s != "" {
out[strings.TrimSpace(k)] = s
}
}
fold := func(dst string, aliases ...string) {
if attrString(out[dst]) != "" {
for _, a := range aliases {
delete(out, a)
}
return
}
for _, a := range aliases {
if s := attrString(out[a]); s != "" {
out[dst] = s
break
}
}
for _, a := range aliases {
delete(out, a)
}
}
fold("id", "eprel_id", "EPRELID", "eprelId")
fold("label", "eprel_label", "eprel_label_url")
fold("pdf", "eprel_pdf", "eprel_pdf_url")
fold("energy_class", "eprel_energy_class", "energyClass")
fold("energy_scale", "eprel_energy_scale", "energyClassRange", "energyScale")
canonical := map[string]any{}
for _, k := range []string{"id", "label", "pdf", "energy_class", "energy_scale"} {
if s := attrString(out[k]); s != "" {
canonical[k] = s
}
}
if len(canonical) == 0 {
return nil
}
return canonical
}
func attrString(v any) string {
if v == nil {
return ""
}
switch t := v.(type) {
case string:
return strings.TrimSpace(t)
case float64:
// JSON numbers for numeric eprel ids.
if t == float64(int64(t)) {
return strings.TrimSpace(fmt.Sprintf("%.0f", t))
}
return strings.TrimSpace(fmt.Sprint(t))
case int:
return fmt.Sprintf("%d", t)
case int64:
return fmt.Sprintf("%d", t)
case map[string]any:
for _, k := range []string{"value", "#text", "text", "id"} {
if s := attrString(t[k]); s != "" {
return s
}
}
return ""
default:
s := strings.TrimSpace(fmt.Sprint(v))
if s == "" || s == "<nil>" {
return ""
}
return s
}
}