Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StandardFieldDef is the subset of standard_fields used while filling gaps.
|
||||
type StandardFieldDef struct {
|
||||
Key string
|
||||
DefaultValue string
|
||||
Unit string
|
||||
MappingHints []string
|
||||
}
|
||||
|
||||
// FillMissingStandardFields copies values from mapped/raw (via key + mapping_hints)
|
||||
// or default_value into mapped when an enabled field is empty.
|
||||
func FillMissingStandardFields(mapped, raw map[string]any, fields []StandardFieldDef) map[string]any {
|
||||
out := mapped
|
||||
if out == nil {
|
||||
out = map[string]any{}
|
||||
} else {
|
||||
// Shallow copy so callers can keep the original.
|
||||
cp := make(map[string]any, len(out)+len(fields))
|
||||
for k, v := range out {
|
||||
cp[k] = v
|
||||
}
|
||||
out = cp
|
||||
}
|
||||
for _, f := range fields {
|
||||
key := strings.TrimSpace(f.Key)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
if valuePresent(out[key]) {
|
||||
continue
|
||||
}
|
||||
candidates := make([]string, 0, 1+len(f.MappingHints))
|
||||
candidates = append(candidates, key)
|
||||
for _, h := range f.MappingHints {
|
||||
h = strings.TrimSpace(h)
|
||||
if h != "" {
|
||||
candidates = append(candidates, h)
|
||||
}
|
||||
}
|
||||
if v := lookupAny(mapped, candidates...); valuePresent(v) {
|
||||
out[key] = normalizeFilled(v, f.Unit)
|
||||
continue
|
||||
}
|
||||
if v := lookupAny(raw, candidates...); valuePresent(v) {
|
||||
out[key] = normalizeFilled(v, f.Unit)
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(f.DefaultValue) != "" {
|
||||
out[key] = normalizeFilled(f.DefaultValue, f.Unit)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func valuePresent(v any) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
switch t := v.(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(t) != ""
|
||||
case []any:
|
||||
return len(t) > 0
|
||||
case map[string]any:
|
||||
return len(t) > 0
|
||||
default:
|
||||
s := strings.TrimSpace(fmt.Sprint(t))
|
||||
return s != "" && s != "<nil>"
|
||||
}
|
||||
}
|
||||
|
||||
func lookupAny(m map[string]any, keys ...string) any {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
lower := map[string]any{}
|
||||
for k, v := range m {
|
||||
lower[strings.ToLower(strings.TrimSpace(k))] = v
|
||||
}
|
||||
for _, k := range keys {
|
||||
lk := strings.ToLower(strings.TrimSpace(k))
|
||||
if v, ok := lower[lk]; ok && valuePresent(v) {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeFilled(v any, unit string) any {
|
||||
switch t := v.(type) {
|
||||
case string:
|
||||
s := SanitizeText(t)
|
||||
if unit != "" && !strings.Contains(strings.ToLower(s), strings.ToLower(unit)) {
|
||||
// Keep numeric values plain; unit stays on the field definition.
|
||||
return s
|
||||
}
|
||||
return s
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func parseHints(v any) []string {
|
||||
switch t := v.(type) {
|
||||
case []string:
|
||||
return t
|
||||
case []any:
|
||||
out := make([]string, 0, len(t))
|
||||
for _, item := range t {
|
||||
if s, ok := item.(string); ok && strings.TrimSpace(s) != "" {
|
||||
out = append(out, strings.TrimSpace(s))
|
||||
}
|
||||
}
|
||||
return out
|
||||
case string:
|
||||
s := strings.TrimSpace(t)
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.Split(s, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p != "" {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user