mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-03-25 13:30:52 +00:00
This commit refactors the KmerSet and related structures to use an immutable K parameter and introduces consistent Copy methods instead of Clone. It also adds attribute API support for KmerSet and KmerSetGroup, and updates persistence logic to handle IDs and metadata correctly.
363 lines
8.5 KiB
Go
363 lines
8.5 KiB
Go
package obikmer
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiutils"
|
|
)
|
|
|
|
// ==================================
|
|
// KMER SET ATTRIBUTE API
|
|
// Mimic BioSequence attribute API from obiseq/attributes.go
|
|
// ==================================
|
|
|
|
// HasAttribute vérifie si une clé d'attribut existe
|
|
func (ks *KmerSet) HasAttribute(key string) bool {
|
|
_, ok := ks.Metadata[key]
|
|
return ok
|
|
}
|
|
|
|
// GetAttribute récupère la valeur d'un attribut
|
|
// Cas particuliers: "id" utilise Id(), "k" utilise K()
|
|
func (ks *KmerSet) GetAttribute(key string) (interface{}, bool) {
|
|
switch key {
|
|
case "id":
|
|
return ks.Id(), true
|
|
case "k":
|
|
return ks.K(), true
|
|
default:
|
|
value, ok := ks.Metadata[key]
|
|
return value, ok
|
|
}
|
|
}
|
|
|
|
// SetAttribute définit la valeur d'un attribut
|
|
// Cas particuliers: "id" utilise SetId(), "k" est immutable (panique)
|
|
func (ks *KmerSet) SetAttribute(key string, value interface{}) {
|
|
switch key {
|
|
case "id":
|
|
if id, ok := value.(string); ok {
|
|
ks.SetId(id)
|
|
} else {
|
|
panic(fmt.Sprintf("id must be a string, got %T", value))
|
|
}
|
|
case "k":
|
|
panic("k is immutable and cannot be modified via SetAttribute")
|
|
default:
|
|
ks.Metadata[key] = value
|
|
}
|
|
}
|
|
|
|
// DeleteAttribute supprime un attribut
|
|
func (ks *KmerSet) DeleteAttribute(key string) {
|
|
delete(ks.Metadata, key)
|
|
}
|
|
|
|
// RemoveAttribute supprime un attribut (alias de DeleteAttribute)
|
|
func (ks *KmerSet) RemoveAttribute(key string) {
|
|
ks.DeleteAttribute(key)
|
|
}
|
|
|
|
// RenameAttribute renomme un attribut
|
|
func (ks *KmerSet) RenameAttribute(newName, oldName string) {
|
|
if value, ok := ks.Metadata[oldName]; ok {
|
|
ks.Metadata[newName] = value
|
|
delete(ks.Metadata, oldName)
|
|
}
|
|
}
|
|
|
|
// GetIntAttribute récupère un attribut en tant qu'entier
|
|
func (ks *KmerSet) GetIntAttribute(key string) (int, bool) {
|
|
value, ok := ks.Metadata[key]
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case int:
|
|
return v, true
|
|
case int64:
|
|
return int(v), true
|
|
case float64:
|
|
return int(v), true
|
|
case string:
|
|
if i, err := strconv.Atoi(v); err == nil {
|
|
return i, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
// GetFloatAttribute récupère un attribut en tant que float64
|
|
func (ks *KmerSet) GetFloatAttribute(key string) (float64, bool) {
|
|
value, ok := ks.Metadata[key]
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case float64:
|
|
return v, true
|
|
case float32:
|
|
return float64(v), true
|
|
case int:
|
|
return float64(v), true
|
|
case int64:
|
|
return float64(v), true
|
|
case string:
|
|
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
|
return f, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
// GetNumericAttribute récupère un attribut numérique (alias de GetFloatAttribute)
|
|
func (ks *KmerSet) GetNumericAttribute(key string) (float64, bool) {
|
|
return ks.GetFloatAttribute(key)
|
|
}
|
|
|
|
// GetStringAttribute récupère un attribut en tant que chaîne
|
|
func (ks *KmerSet) GetStringAttribute(key string) (string, bool) {
|
|
value, ok := ks.Metadata[key]
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case string:
|
|
return v, true
|
|
default:
|
|
return fmt.Sprintf("%v", v), true
|
|
}
|
|
}
|
|
|
|
// GetBoolAttribute récupère un attribut en tant que booléen
|
|
func (ks *KmerSet) GetBoolAttribute(key string) (bool, bool) {
|
|
value, ok := ks.Metadata[key]
|
|
if !ok {
|
|
return false, false
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case bool:
|
|
return v, true
|
|
case int:
|
|
return v != 0, true
|
|
case string:
|
|
if b, err := strconv.ParseBool(v); err == nil {
|
|
return b, true
|
|
}
|
|
}
|
|
return false, false
|
|
}
|
|
|
|
// AttributeKeys retourne l'ensemble des clés d'attributs
|
|
func (ks *KmerSet) AttributeKeys() obiutils.Set[string] {
|
|
keys := obiutils.MakeSet[string]()
|
|
for key := range ks.Metadata {
|
|
keys.Add(key)
|
|
}
|
|
return keys
|
|
}
|
|
|
|
// Keys retourne l'ensemble des clés d'attributs (alias de AttributeKeys)
|
|
func (ks *KmerSet) Keys() obiutils.Set[string] {
|
|
return ks.AttributeKeys()
|
|
}
|
|
|
|
// ==================================
|
|
// KMER SET GROUP ATTRIBUTE API
|
|
// Métadonnées du groupe + accès via Get() pour les sets individuels
|
|
// ==================================
|
|
|
|
// HasAttribute vérifie si une clé d'attribut existe pour le groupe
|
|
func (ksg *KmerSetGroup) HasAttribute(key string) bool {
|
|
_, ok := ksg.Metadata[key]
|
|
return ok
|
|
}
|
|
|
|
// GetAttribute récupère la valeur d'un attribut du groupe
|
|
// Cas particuliers: "id" utilise Id(), "k" utilise K()
|
|
func (ksg *KmerSetGroup) GetAttribute(key string) (interface{}, bool) {
|
|
switch key {
|
|
case "id":
|
|
return ksg.Id(), true
|
|
case "k":
|
|
return ksg.K(), true
|
|
default:
|
|
value, ok := ksg.Metadata[key]
|
|
return value, ok
|
|
}
|
|
}
|
|
|
|
// SetAttribute définit la valeur d'un attribut du groupe
|
|
// Cas particuliers: "id" utilise SetId(), "k" est immutable (panique)
|
|
func (ksg *KmerSetGroup) SetAttribute(key string, value interface{}) {
|
|
switch key {
|
|
case "id":
|
|
if id, ok := value.(string); ok {
|
|
ksg.SetId(id)
|
|
} else {
|
|
panic(fmt.Sprintf("id must be a string, got %T", value))
|
|
}
|
|
case "k":
|
|
panic("k is immutable and cannot be modified via SetAttribute")
|
|
default:
|
|
ksg.Metadata[key] = value
|
|
}
|
|
}
|
|
|
|
// DeleteAttribute supprime un attribut du groupe
|
|
func (ksg *KmerSetGroup) DeleteAttribute(key string) {
|
|
delete(ksg.Metadata, key)
|
|
}
|
|
|
|
// RemoveAttribute supprime un attribut du groupe (alias)
|
|
func (ksg *KmerSetGroup) RemoveAttribute(key string) {
|
|
ksg.DeleteAttribute(key)
|
|
}
|
|
|
|
// RenameAttribute renomme un attribut du groupe
|
|
func (ksg *KmerSetGroup) RenameAttribute(newName, oldName string) {
|
|
if value, ok := ksg.Metadata[oldName]; ok {
|
|
ksg.Metadata[newName] = value
|
|
delete(ksg.Metadata, oldName)
|
|
}
|
|
}
|
|
|
|
// GetIntAttribute récupère un attribut entier du groupe
|
|
func (ksg *KmerSetGroup) GetIntAttribute(key string) (int, bool) {
|
|
value, ok := ksg.GetAttribute(key)
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case int:
|
|
return v, true
|
|
case int64:
|
|
return int(v), true
|
|
case float64:
|
|
return int(v), true
|
|
case string:
|
|
if i, err := strconv.Atoi(v); err == nil {
|
|
return i, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
// GetFloatAttribute récupère un attribut float64 du groupe
|
|
func (ksg *KmerSetGroup) GetFloatAttribute(key string) (float64, bool) {
|
|
value, ok := ksg.GetAttribute(key)
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case float64:
|
|
return v, true
|
|
case float32:
|
|
return float64(v), true
|
|
case int:
|
|
return float64(v), true
|
|
case int64:
|
|
return float64(v), true
|
|
case string:
|
|
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
|
return f, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
// GetNumericAttribute récupère un attribut numérique du groupe
|
|
func (ksg *KmerSetGroup) GetNumericAttribute(key string) (float64, bool) {
|
|
return ksg.GetFloatAttribute(key)
|
|
}
|
|
|
|
// GetStringAttribute récupère un attribut chaîne du groupe
|
|
func (ksg *KmerSetGroup) GetStringAttribute(key string) (string, bool) {
|
|
value, ok := ksg.GetAttribute(key)
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case string:
|
|
return v, true
|
|
default:
|
|
return fmt.Sprintf("%v", v), true
|
|
}
|
|
}
|
|
|
|
// GetBoolAttribute récupère un attribut booléen du groupe
|
|
func (ksg *KmerSetGroup) GetBoolAttribute(key string) (bool, bool) {
|
|
value, ok := ksg.GetAttribute(key)
|
|
if !ok {
|
|
return false, false
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case bool:
|
|
return v, true
|
|
case int:
|
|
return v != 0, true
|
|
case string:
|
|
if b, err := strconv.ParseBool(v); err == nil {
|
|
return b, true
|
|
}
|
|
}
|
|
return false, false
|
|
}
|
|
|
|
// AttributeKeys retourne l'ensemble des clés d'attributs du groupe
|
|
func (ksg *KmerSetGroup) AttributeKeys() obiutils.Set[string] {
|
|
keys := obiutils.MakeSet[string]()
|
|
for key := range ksg.Metadata {
|
|
keys.Add(key)
|
|
}
|
|
return keys
|
|
}
|
|
|
|
// Keys retourne l'ensemble des clés d'attributs du groupe (alias)
|
|
func (ksg *KmerSetGroup) Keys() obiutils.Set[string] {
|
|
return ksg.AttributeKeys()
|
|
}
|
|
|
|
// ==================================
|
|
// MÉTHODES POUR ACCÉDER AUX ATTRIBUTS DES SETS INDIVIDUELS VIA Get()
|
|
// Architecture zero-copy: ksg.Get(i).SetAttribute(...)
|
|
// ==================================
|
|
|
|
// Exemple d'utilisation:
|
|
// Pour accéder aux métadonnées d'un KmerSet individuel dans un groupe:
|
|
// ks := ksg.Get(0)
|
|
// ks.SetAttribute("level", 1)
|
|
// hasLevel := ks.HasAttribute("level")
|
|
//
|
|
// Pour les métadonnées du groupe:
|
|
// ksg.SetAttribute("name", "FrequencyFilter")
|
|
// name, ok := ksg.GetStringAttribute("name")
|
|
|
|
// AllAttributeKeys retourne toutes les clés d'attributs uniques du groupe ET de tous ses sets
|
|
func (ksg *KmerSetGroup) AllAttributeKeys() obiutils.Set[string] {
|
|
keys := obiutils.MakeSet[string]()
|
|
|
|
// Ajouter les clés du groupe
|
|
for key := range ksg.Metadata {
|
|
keys.Add(key)
|
|
}
|
|
|
|
// Ajouter les clés de chaque set
|
|
for _, ks := range ksg.sets {
|
|
for key := range ks.Metadata {
|
|
keys.Add(key)
|
|
}
|
|
}
|
|
|
|
return keys
|
|
}
|