diff --git a/timespans/activationperiod.go b/timespans/activationperiod.go
index 2dd6fdf47..5ff23fd77 100644
--- a/timespans/activationperiod.go
+++ b/timespans/activationperiod.go
@@ -20,6 +20,8 @@ package timespans
import (
"time"
//"log"
+ "strconv"
+ "strings"
)
/*
@@ -40,3 +42,57 @@ func (ap *ActivationPeriod) AddInterval(is ...*Interval) {
}
}
+/*
+Serializes the activation periods for the storage. Used for key-value storages.
+*/
+func (ap *ActivationPeriod) store() (result string) {
+ result += strconv.FormatInt(ap.ActivationTime.UnixNano(), 10) + ";"
+ for _, i := range ap.Intervals {
+ var is string
+ is = strconv.Itoa(int(i.Month)) + "|"
+ is += strconv.Itoa(i.MonthDay) + "|"
+ for _, wd := range i.WeekDays {
+ is += strconv.Itoa(int(wd)) + ","
+ }
+ is = strings.TrimRight(is, ",") + "|"
+ is += i.StartTime + "|"
+ is += i.EndTime + "|"
+ is += strconv.FormatFloat(i.Ponder, 'f', -1, 64) + "|"
+ is += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + "|"
+ is += strconv.FormatFloat(i.Price, 'f', -1, 64) + "|"
+ is += strconv.FormatFloat(i.BillingUnit, 'f', -1, 64)
+ result += is + ";"
+ }
+ return
+}
+
+/*
+De-serializes the activation periods for the storage. Used for key-value storages.
+*/
+func (ap *ActivationPeriod) restore(input string) {
+ elements := strings.Split(input, ";")
+ unixNano, _ := strconv.ParseInt(elements[0], 10, 64)
+ ap.ActivationTime = time.Unix(0, unixNano).In(time.UTC)
+ ap.Intervals = make([]*Interval, 0)
+ for _, is := range elements[1 : len(elements)-1] {
+ i := &Interval{}
+ ise := strings.Split(is, "|")
+ month, _ := strconv.Atoi(ise[0])
+ i.Month = time.Month(month)
+ i.MonthDay, _ = strconv.Atoi(ise[1])
+ for _, d := range strings.Split(ise[2], ",") {
+ if d != "" {
+ wd, _ := strconv.Atoi(d)
+ i.WeekDays = append(i.WeekDays, time.Weekday(wd))
+ }
+ }
+ i.StartTime = ise[3]
+ i.EndTime = ise[4]
+ i.Ponder, _ = strconv.ParseFloat(ise[5], 64)
+ i.ConnectFee, _ = strconv.ParseFloat(ise[6], 64)
+ i.Price, _ = strconv.ParseFloat(ise[7], 64)
+ i.BillingUnit, _ = strconv.ParseFloat(ise[8], 64)
+
+ ap.Intervals = append(ap.Intervals, i)
+ }
+}
diff --git a/timespans/activationperiod_test.go b/timespans/activationperiod_test.go
index 8f5b62244..24cc2947a 100644
--- a/timespans/activationperiod_test.go
+++ b/timespans/activationperiod_test.go
@@ -47,8 +47,6 @@ func TestApRestoreRedis(t *testing.T) {
}
func TestApStoreRestore(t *testing.T) {
- getter, _ := NewKyotoStorage("test.kch")
- defer getter.Close()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Month: time.February,
MonthDay: 1,
@@ -57,22 +55,29 @@ func TestApStoreRestore(t *testing.T) {
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
-
- getter.SetActivationPeriods("storerestore", []*ActivationPeriod{ap})
- aps, err := getter.GetActivationPeriods("storerestore")
- if err != nil || len(aps) != 1 || !reflect.DeepEqual(ap, aps[0]) {
- t.Log(aps)
- t.Errorf("Expected %v was %v ", ap, aps)
+ result := ap.store()
+ expected := "1328106601000000000;2|1|3,4|14:30:00|15:00:00|0|0|0|0;"
+ if result != expected {
+ t.Errorf("Expected %q was %q", expected, result)
+ }
+ ap1 := ActivationPeriod{}
+ ap1.restore(result)
+ if reflect.DeepEqual(ap, ap1) {
+ t.Errorf("Expected %v was %v", ap, ap1)
}
-
}
/**************************** Benchmarks *************************************/
+func BenchmarkActivationPeriodRestore(b *testing.B) {
+ ap := ActivationPeriod{}
+ for i := 0; i < b.N; i++ {
+ ap.restore("1328106601;2|1|3,4|14:30:00|15:00:00|0|0|0|0;")
+ }
+}
+
func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
b.StopTimer()
- getter, _ := NewKyotoStorage("test.kch")
- defer getter.Close()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Month: time.February,
MonthDay: 1,
@@ -82,9 +87,10 @@ func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
+ ap1 := ActivationPeriod{}
b.StartTimer()
for i := 0; i < b.N; i++ {
- getter.SetActivationPeriods("storerestore", []*ActivationPeriod{ap})
- getter.GetActivationPeriods("storerestore")
+ result := ap.store()
+ ap1.restore(result)
}
}
diff --git a/timespans/calldesc_test.go b/timespans/calldesc_test.go
index 5b59604e1..be3db32e7 100644
--- a/timespans/calldesc_test.go
+++ b/timespans/calldesc_test.go
@@ -221,7 +221,7 @@ func TestMaxSessionTimeNoUserBudget(t *testing.T) {
defer getter.Close()
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", storageGetter: getter, Amount: 1000}
result, err := cd.GetMaxSessionTime()
- if result != 1000 || err == nil {
+ if result != 1000 || err != nil {
t.Errorf("Expected %v was %v", 1000, result)
}
}
diff --git a/timespans/destinations.go b/timespans/destinations.go
index b0f4ea2e5..556924f38 100644
--- a/timespans/destinations.go
+++ b/timespans/destinations.go
@@ -17,6 +17,10 @@ along with this program. If not, see
*/
package timespans
+import (
+ "strings"
+)
+
/*
Structure that gathers multiple destination prefixes under a common id.
*/
@@ -25,6 +29,21 @@ type Destination struct {
Prefixes []string
}
+/*
+Serializes the destination for the storage. Used for key-value storages.
+*/
+func (d *Destination) store() (result string) {
+ for _, p := range d.Prefixes {
+ result += p + ","
+ }
+ result = strings.TrimRight(result, ",")
+ return
+}
+
+func (d *Destination) restore(input string) {
+ d.Prefixes = strings.Split(input, ",")
+}
+
/*
De-serializes the destination for the storage. Used for key-value storages.
*/
diff --git a/timespans/kyoto_storage.go b/timespans/kyoto_storage.go
index d88f2f0a2..68a638fb1 100644
--- a/timespans/kyoto_storage.go
+++ b/timespans/kyoto_storage.go
@@ -15,123 +15,83 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
*/
+
package timespans
import (
- "bitbucket.org/ww/cabinet"
- "bytes"
- "encoding/json"
+ "github.com/fsouza/gokabinet/kc"
//"log"
- "sync"
+ "strings"
)
type KyotoStorage struct {
- //db *kc.DB
- db *cabinet.KCDB
- buf bytes.Buffer
- dec *json.Decoder
- enc *json.Encoder
- mux sync.Mutex // we need norma lock because we reset the buf variable
+ db *kc.DB
}
func NewKyotoStorage(filaName string) (*KyotoStorage, error) {
- ndb := cabinet.New()
- err := ndb.Open(filaName, cabinet.KCOWRITER|cabinet.KCOCREATE)
- ks := &KyotoStorage{db: ndb}
-
- ks.dec = json.NewDecoder(&ks.buf)
- ks.enc = json.NewEncoder(&ks.buf)
- return ks, err
+ ndb, err := kc.Open(filaName, kc.WRITE)
+ return &KyotoStorage{db: ndb}, err
}
func (ks *KyotoStorage) Close() {
ks.db.Close()
}
-func (ks *KyotoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
- ks.mux.Lock()
- defer ks.mux.Unlock()
+func (ks *KyotoStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
+ values, err := ks.db.Get(key)
- ks.buf.Reset()
- ks.enc.Encode(aps)
- return ks.db.Set([]byte(key), ks.buf.Bytes())
+ if err == nil {
+ for _, ap_string := range strings.Split(values, "\n") {
+ if len(ap_string) > 0 {
+ ap := &ActivationPeriod{}
+ ap.restore(ap_string)
+ aps = append(aps, ap)
+ }
+ }
+ }
+ return aps, err
}
-func (ks *KyotoStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
- ks.mux.Lock()
- defer ks.mux.Unlock()
+func (ks *KyotoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
+ result := ""
+ for _, ap := range aps {
+ result += ap.store() + "\n"
+ }
+ return ks.db.Set(key, result)
+}
- values, err := ks.db.Get([]byte(key))
- if err == nil {
- ks.buf.Reset()
- ks.buf.Write(values)
- err = ks.dec.Decode(&aps)
+func (ks *KyotoStorage) GetDestination(key string) (dest *Destination, err error) {
+ if values, err := ks.db.Get(key); err == nil {
+ dest = &Destination{Id: key}
+ dest.restore(values)
}
return
}
func (ks *KyotoStorage) SetDestination(dest *Destination) error {
- ks.mux.Lock()
- defer ks.mux.Unlock()
-
- ks.buf.Reset()
- ks.enc.Encode(dest)
- return ks.db.Set([]byte(dest.Id), ks.buf.Bytes())
+ return ks.db.Set(dest.Id, dest.store())
}
-func (ks *KyotoStorage) GetDestination(key string) (dest *Destination, err error) {
- ks.mux.Lock()
- defer ks.mux.Unlock()
-
- values, err := ks.db.Get([]byte(key))
- if err == nil {
- ks.buf.Reset()
- ks.buf.Write(values)
- err = ks.dec.Decode(&dest)
+func (ks *KyotoStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
+ if values, err := ks.db.Get(key); err == nil {
+ tp = &TariffPlan{Id: key}
+ tp.restore(values)
}
return
}
func (ks *KyotoStorage) SetTariffPlan(tp *TariffPlan) error {
- ks.mux.Lock()
- defer ks.mux.Unlock()
-
- ks.buf.Reset()
- ks.enc.Encode(tp)
- return ks.db.Set([]byte(tp.Id), ks.buf.Bytes())
+ return ks.db.Set(tp.Id, tp.store())
}
-func (ks *KyotoStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
- ks.mux.Lock()
- defer ks.mux.Unlock()
-
- values, err := ks.db.Get([]byte(key))
- if err == nil {
- ks.buf.Reset()
- ks.buf.Write(values)
- err = ks.dec.Decode(&tp)
+func (ks *KyotoStorage) GetUserBudget(key string) (ub *UserBudget, err error) {
+ if values, err := ks.db.Get(key); err == nil {
+ ub = &UserBudget{Id: key}
+ ub.restore(values)
}
return
}
func (ks *KyotoStorage) SetUserBudget(ub *UserBudget) error {
- ks.mux.Lock()
- defer ks.mux.Unlock()
-
- ks.buf.Reset()
- ks.enc.Encode(ub)
- return ks.db.Set([]byte(ub.Id), ks.buf.Bytes())
-}
-
-func (ks *KyotoStorage) GetUserBudget(key string) (ub *UserBudget, err error) {
- ks.mux.Lock()
- defer ks.mux.Unlock()
-
- values, err := ks.db.Get([]byte(key))
- if err == nil {
- ks.buf.Reset()
- ks.buf.Write(values)
- ks.dec.Decode(&ub)
- }
- return
+ return ks.db.Set(ub.Id, ub.store())
}
diff --git a/timespans/minute_buckets.go b/timespans/minute_buckets.go
index c24821e2f..908c7fcc1 100644
--- a/timespans/minute_buckets.go
+++ b/timespans/minute_buckets.go
@@ -20,6 +20,8 @@ package timespans
import (
// "log"
"math"
+ "strconv"
+ "strings"
)
type MinuteBucket struct {
@@ -31,6 +33,28 @@ type MinuteBucket struct {
precision int
}
+/*
+Serializes the minute bucket for the storage. Used for key-value storages.
+*/
+func (mb *MinuteBucket) store() (result string) {
+ result += strconv.Itoa(int(mb.Seconds)) + "|"
+ result += strconv.Itoa(int(mb.Priority)) + "|"
+ result += strconv.FormatFloat(mb.Price, 'f', -1, 64) + "|"
+ result += mb.DestinationId
+ return
+}
+
+/*
+De-serializes the minute bucket for the storage. Used for key-value storages.
+*/
+func (mb *MinuteBucket) restore(input string) {
+ elements := strings.Split(input, "|")
+ mb.Seconds, _ = strconv.ParseFloat(elements[0], 64)
+ mb.Priority, _ = strconv.Atoi(elements[1])
+ mb.Price, _ = strconv.ParseFloat(elements[2], 64)
+ mb.DestinationId = elements[3]
+}
+
/*
Returns the destination loading it from the storage if necessary.
*/
diff --git a/timespans/redis_storage.go b/timespans/redis_storage.go
index 93bd64b24..66c225793 100644
--- a/timespans/redis_storage.go
+++ b/timespans/redis_storage.go
@@ -18,126 +18,86 @@ along with this program. If not, see
package timespans
import (
- "bytes"
- "encoding/json"
"github.com/simonz05/godis"
- // "log"
- "sync"
+ "strings"
)
type RedisStorage struct {
dbNb int
db *godis.Client
- buf bytes.Buffer
- dec *json.Decoder
- enc *json.Encoder
- mux sync.Mutex
}
func NewRedisStorage(address string, db int) (*RedisStorage, error) {
ndb := godis.New(address, db, "")
- rs := &RedisStorage{db: ndb, dbNb: db}
- rs.dec = json.NewDecoder(&rs.buf)
- rs.enc = json.NewEncoder(&rs.buf)
- return rs, nil
+ return &RedisStorage{db: ndb, dbNb: db}, nil
}
func (rs *RedisStorage) Close() {
rs.db.Quit()
}
-func (rs *RedisStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
- //.db.Select(rs.dbNb)
- rs.mux.Lock()
- defer rs.mux.Unlock()
-
- rs.buf.Reset()
- rs.enc.Encode(aps)
- return rs.db.Set(key, rs.buf.Bytes())
-}
-
func (rs *RedisStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
//rs.db.Select(rs.dbNb)
- rs.mux.Lock()
- defer rs.mux.Unlock()
elem, err := rs.db.Get(key)
+ values := elem.String()
if err == nil {
- rs.buf.Reset()
- rs.buf.Write(elem.Bytes())
-
- err = rs.dec.Decode(&aps)
+ for _, ap_string := range strings.Split(values, "\n") {
+ if len(ap_string) > 0 {
+ ap := &ActivationPeriod{}
+ ap.restore(ap_string)
+ aps = append(aps, ap)
+ }
+ }
}
- return
+ return aps, err
}
-func (rs *RedisStorage) SetDestination(dest *Destination) error {
- //rs.db.Select(rs.dbNb + 1)
- rs.mux.Lock()
- defer rs.mux.Unlock()
-
- rs.buf.Reset()
- rs.enc.Encode(dest)
- return rs.db.Set(dest.Id, rs.buf.Bytes())
+func (rs *RedisStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
+ //.db.Select(rs.dbNb)
+ result := ""
+ for _, ap := range aps {
+ result += ap.store() + "\n"
+ }
+ return rs.db.Set(key, result)
}
func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error) {
//rs.db.Select(rs.dbNb + 1)
- rs.mux.Lock()
- defer rs.mux.Unlock()
+ if values, err := rs.db.Get(key); err == nil {
+ dest = &Destination{Id: key}
+ dest.restore(values.String())
+ }
+ return
+}
+func (rs *RedisStorage) SetDestination(dest *Destination) error {
+ //rs.db.Select(rs.dbNb + 1)
+ return rs.db.Set(dest.Id, dest.store())
+}
- elem, err := rs.db.Get(key)
- if err == nil {
- rs.buf.Reset()
- rs.buf.Write(elem.Bytes())
- err = rs.dec.Decode(&dest)
+func (rs *RedisStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
+ //rs.db.Select(rs.dbNb + 2)
+ if values, err := rs.db.Get(key); err == nil {
+ tp = &TariffPlan{Id: key}
+ tp.restore(values.String())
}
return
}
func (rs *RedisStorage) SetTariffPlan(tp *TariffPlan) error {
//rs.db.Select(rs.dbNb + 2)
- rs.mux.Lock()
- defer rs.mux.Unlock()
-
- rs.buf.Reset()
- rs.enc.Encode(tp)
- return rs.db.Set(tp.Id, rs.buf.Bytes())
+ return rs.db.Set(tp.Id, tp.store())
}
-func (rs *RedisStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
- //rs.db.Select(rs.dbNb + 2)
- rs.mux.Lock()
- defer rs.mux.Unlock()
-
- elem, err := rs.db.Get(key)
- if err == nil {
- rs.buf.Reset()
- rs.buf.Write(elem.Bytes())
- err = rs.dec.Decode(&tp)
+func (rs *RedisStorage) GetUserBudget(key string) (ub *UserBudget, err error) {
+ //rs.db.Select(rs.dbNb + 3)
+ if values, err := rs.db.Get(key); err == nil {
+ ub = &UserBudget{Id: key}
+ ub.restore(values.String())
}
return
}
func (rs *RedisStorage) SetUserBudget(ub *UserBudget) error {
//rs.db.Select(rs.dbNb + 3)
- rs.mux.Lock()
- defer rs.mux.Unlock()
-
- rs.buf.Reset()
- rs.enc.Encode(ub)
- return rs.db.Set(ub.Id, rs.buf.Bytes())
-}
-
-func (rs *RedisStorage) GetUserBudget(key string) (ub *UserBudget, err error) {
- //rs.db.Select(rs.dbNb + 3)
- rs.mux.Lock()
- defer rs.mux.Unlock()
-
- elem, err := rs.db.Get(key)
- if err == nil {
- rs.buf.Reset()
- rs.buf.Write(elem.Bytes())
- err = rs.dec.Decode(&ub)
- }
- return
+ return rs.db.Set(ub.Id, ub.store())
}
diff --git a/timespans/tariff_plans.go b/timespans/tariff_plans.go
index fb642f5a8..8872deceb 100644
--- a/timespans/tariff_plans.go
+++ b/timespans/tariff_plans.go
@@ -19,6 +19,8 @@ package timespans
import (
// "log"
+ "strings"
+ "strconv"
)
/*
@@ -35,6 +37,62 @@ type TariffPlan struct {
VolumeDiscountThresholds []*VolumeDiscount
}
+/*
+Serializes the tariff plan for the storage. Used for key-value storages.
+*/
+func (tp *TariffPlan) store() (result string) {
+ result += strconv.FormatFloat(tp.SmsCredit, 'f', -1, 64) + ";"
+ result += strconv.FormatFloat(tp.Traffic, 'f', -1, 64) + ";"
+ result += strconv.FormatFloat(tp.ReceivedCallSecondsLimit, 'f', -1, 64) + ";"
+ if tp.RecivedCallBonus == nil {
+ tp.RecivedCallBonus = &RecivedCallBonus{}
+ }
+ result += tp.RecivedCallBonus.store() + ";"
+ for i, mb := range tp.MinuteBuckets {
+ if i > 0 {
+ result += ","
+ }
+ result += mb.store()
+ }
+ if tp.VolumeDiscountThresholds != nil {
+ result += ";"
+ }
+ for i, vd := range tp.VolumeDiscountThresholds {
+ if i > 0 {
+ result += ","
+ }
+ result += strconv.FormatFloat(vd.Volume, 'f', -1, 64) + "|" + strconv.FormatFloat(vd.Discount, 'f', -1, 64)
+ }
+ result = strings.TrimRight(result, ";")
+ return
+}
+
+/*
+De-serializes the tariff plan for the storage. Used for key-value storages.
+*/
+func (tp *TariffPlan) restore(input string) {
+ elements := strings.Split(input, ";")
+ tp.SmsCredit, _ = strconv.ParseFloat(elements[0], 64)
+ tp.Traffic, _ = strconv.ParseFloat(elements[1], 64)
+ tp.ReceivedCallSecondsLimit, _ = strconv.ParseFloat(elements[2], 64)
+ tp.RecivedCallBonus = &RecivedCallBonus{}
+ tp.RecivedCallBonus.restore(elements[3])
+ for _, mbs := range strings.Split(elements[4], ",") {
+ mb := &MinuteBucket{}
+ mb.restore(mbs)
+ tp.MinuteBuckets = append(tp.MinuteBuckets, mb)
+ }
+ if len(elements) > 5 {
+ for _, vdss := range strings.Split(elements[5], ",") {
+ vd := &VolumeDiscount{}
+ vds := strings.Split(vdss, "|")
+ vd.Volume, _ = strconv.ParseFloat(vds[0], 64)
+ vd.Discount, _ = strconv.ParseFloat(vds[1], 64)
+ tp.VolumeDiscountThresholds = append(tp.VolumeDiscountThresholds, vd)
+ }
+ }
+}
+
/*
Structure that holds the thresholds and for which
*/
@@ -52,3 +110,31 @@ type RecivedCallBonus struct {
Traffic float64
MinuteBucket *MinuteBucket
}
+
+/*
+Serializes the tariff plan for the storage. Used for key-value storages.
+*/
+func (rcb *RecivedCallBonus) store() (result string) {
+ result += strconv.FormatFloat(rcb.Credit, 'f', -1, 64) + ","
+ result += strconv.FormatFloat(rcb.SmsCredit, 'f', -1, 64) + ","
+ result += strconv.FormatFloat(rcb.Traffic, 'f', -1, 64)
+ if rcb.MinuteBucket != nil {
+ result += ","
+ result += rcb.MinuteBucket.store()
+ }
+ return
+}
+
+/*
+De-serializes the tariff plan for the storage. Used for key-value storages.
+*/
+func (rcb *RecivedCallBonus) restore(input string) {
+ elements := strings.Split(input, ",")
+ rcb.Credit, _ = strconv.ParseFloat(elements[0], 64)
+ rcb.SmsCredit, _ = strconv.ParseFloat(elements[1], 64)
+ rcb.Traffic, _ = strconv.ParseFloat(elements[2], 64)
+ if len(elements) > 3 {
+ rcb.MinuteBucket = &MinuteBucket{}
+ rcb.MinuteBucket.restore(elements[3])
+ }
+}
diff --git a/timespans/test.kch b/timespans/test.kch
index 5fd8c3b57..d7a0230fd 100644
Binary files a/timespans/test.kch and b/timespans/test.kch differ
diff --git a/timespans/userbudget.go b/timespans/userbudget.go
index 36de8884c..8de90f29f 100644
--- a/timespans/userbudget.go
+++ b/timespans/userbudget.go
@@ -21,6 +21,8 @@ import (
// "log"
"sort"
"sync"
+ "strconv"
+ "strings"
)
/*
@@ -68,6 +70,50 @@ func (bs bucketsorter) Less(j, i int) bool {
bs[i].Price > bs[j].Price
}
+/*
+Serializes the user budget for the storage. Used for key-value storages.
+*/
+func (ub *UserBudget) store() (result string) {
+ result += strconv.FormatFloat(ub.Credit, 'f', -1, 64) + ";"
+ result += strconv.FormatFloat(ub.SmsCredit, 'f', -1, 64) + ";"
+ result += strconv.FormatFloat(ub.Traffic, 'f', -1, 64) + ";"
+ result += strconv.FormatFloat(ub.VolumeDiscountSeconds, 'f', -1, 64) + ";"
+ result += strconv.FormatFloat(ub.ReceivedCallSeconds, 'f', -1, 64) + ";"
+ result += strconv.Itoa(ub.ResetDayOfTheMonth) + ";"
+ result += ub.TariffPlanId
+ if ub.MinuteBuckets != nil {
+ result += ";"
+ }
+ for i, mb := range ub.MinuteBuckets {
+ if i > 0 {
+ result += ","
+ }
+ result += mb.store()
+ }
+ return
+}
+
+/*
+De-serializes the user budget for the storage. Used for key-value storages.
+*/
+func (ub *UserBudget) restore(input string) {
+ elements := strings.Split(input, ";")
+ ub.Credit, _ = strconv.ParseFloat(elements[0], 64)
+ ub.SmsCredit, _ = strconv.ParseFloat(elements[1], 64)
+ ub.Traffic, _ = strconv.ParseFloat(elements[2], 64)
+ ub.VolumeDiscountSeconds, _ = strconv.ParseFloat(elements[3], 64)
+ ub.ReceivedCallSeconds, _ = strconv.ParseFloat(elements[4], 64)
+ ub.ResetDayOfTheMonth, _ = strconv.Atoi(elements[5])
+ ub.TariffPlanId = elements[6]
+ if len(elements) > 7 {
+ for _, mbs := range strings.Split(elements[7], ",") {
+ mb := &MinuteBucket{}
+ mb.restore(mbs)
+ ub.MinuteBuckets = append(ub.MinuteBuckets, mb)
+ }
+ }
+}
+
/*
Returns the tariff plan loading it from the storage if necessary.
*/