workin on rating profiles in db

This commit is contained in:
Radu Ioan Fericean
2012-08-17 16:24:24 +03:00
parent f6725a0435
commit f068a09515
13 changed files with 225 additions and 184 deletions

View File

@@ -97,34 +97,3 @@ func (ap *ActivationPeriod) restore(input string) {
ap.Intervals = append(ap.Intervals, i)
}
}
type RatingProfile struct {
Id string `bson:"_id,omitempty"`
DestinationInfo string
FallbackKey string
ActivationPeriods []*ActivationPeriod
}
func (rp *RatingProfile) store() (result string) {
result += rp.Id + ">"
result += rp.DestinationInfo + ">"
result += rp.FallbackKey + ">"
for _, ap := range rp.ActivationPeriods {
result += ap.store() + "<"
}
result = strings.TrimRight(result, "<")
return
}
func (rp *RatingProfile) restore(input string) {
elements := strings.Split(input, ">")
rp.Id = elements[0]
rp.DestinationInfo = elements[1]
rp.FallbackKey = elements[2]
apsList := strings.Split(elements[3], "<")
for _, aps := range apsList {
ap := new(ActivationPeriod)
ap.restore(aps)
rp.ActivationPeriods = append(rp.ActivationPeriods, ap)
}
}

View File

@@ -64,7 +64,7 @@ func TestApRestoreFromStorage(t *testing.T) {
Tenant: "CUSTOMER_1",
Subject: "rif:from:tm",
Destination: "49"}
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 2 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
}
@@ -110,7 +110,7 @@ func TestApStoreRestoreBlank(t *testing.T) {
func TestFallbackDirect(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "41"}
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
}
@@ -118,7 +118,7 @@ func TestFallbackDirect(t *testing.T) {
func TestFallbackWithBackTrace(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "4123"}
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
}
@@ -126,7 +126,7 @@ func TestFallbackWithBackTrace(t *testing.T) {
func TestFallbackDefault(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "4123"}
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
}
@@ -134,7 +134,7 @@ func TestFallbackDefault(t *testing.T) {
func TestFallbackNoInfiniteLoop(t *testing.T) {
cd := &CallDescriptor{Tenant: "vdf", Subject: "rif", Destination: "0721"}
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 0 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
}

View File

@@ -36,11 +36,8 @@ func init() {
}
const (
// the minimum length for a destination prefix to be matched.
MinPrefixLength = 2
RecursionMaxDepth = 4
FallbackDestination = "fallback" // the string to be used to mark the fallback destination
FallbackSubject = "*all"
RECURSION_MAX_DEPTH = 4
FALLBACK_SUBJECT = "*all"
)
var (
@@ -80,7 +77,6 @@ type CallDescriptor struct {
Amount float64
FallbackSubject string // the subject to check for destination if not found on primary subject
ActivationPeriods []*ActivationPeriod
FallbackKey string
userBalance *UserBalance
}
@@ -89,22 +85,6 @@ func (cd *CallDescriptor) AddActivationPeriod(aps ...*ActivationPeriod) {
cd.ActivationPeriods = append(cd.ActivationPeriods, aps...)
}
// Adds an activation period that applyes to current call descriptor if not already present.
func (cd *CallDescriptor) AddActivationPeriodIfNotPresent(aps ...*ActivationPeriod) {
for _, ap := range aps {
found := false
for _, eap := range cd.ActivationPeriods {
if ap.Equal(eap) {
found = true
break
}
}
if !found {
cd.ActivationPeriods = append(cd.ActivationPeriods, ap)
}
}
}
// Returns the key used to retrive the user balance involved in this call
func (cd *CallDescriptor) GetUserBalanceKey() string {
subj := cd.Subject
@@ -148,31 +128,18 @@ func SetDebitPeriod(d time.Duration) {
/*
Restores the activation periods for the specified prefix from storage.
*/
func (cd *CallDescriptor) SearchStorageForPrefix() (destPrefix string, err error) {
func (cd *CallDescriptor) LoadActivationPeriods() (destPrefix string, err error) {
if val, err := cache.GetXCached(cd.GetKey()); err == nil {
xaps := val.(xCachedActivationPeriods)
cd.ActivationPeriods = xaps.aps
return xaps.destPrefix, nil
}
cd.ActivationPeriods = make([]*ActivationPeriod, 0)
base := fmt.Sprintf("%s:%s:%s:%s:", cd.Direction, cd.Tenant, cd.TOR, cd.Subject)
destPrefix = cd.Destination
key := base + destPrefix
values, err := cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1)
destPrefix, values, err := cd.getActivationPeriodsForPrefix(cd.GetKey(), cd.Destination, 1)
if err != nil {
// use the default destination
key := base + FallbackDestination
values, err = cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1)
}
if err != nil {
// use the default subject
base = fmt.Sprintf("%s:%s:%s:%s:", cd.Direction, cd.Tenant, cd.TOR, FallbackSubject)
key = base + destPrefix
values, err = cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1)
if err != nil {
// use the default destination
key := base + FallbackDestination
values, err = cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1)
fallbackKey := fmt.Sprintf("%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, FALLBACK_SUBJECT)
// use the default subject
destPrefix, values, err = cd.getActivationPeriodsForPrefix(fallbackKey, cd.Destination, 1)
}
}
//load the activation preriods
@@ -184,37 +151,23 @@ func (cd *CallDescriptor) SearchStorageForPrefix() (destPrefix string, err error
return
}
func (cd *CallDescriptor) getRatinPprofile(key, base, destPrefix string, recursionDepth int) (values []*ActivationPeriod, err error) {
if recursionDepth > RecursionMaxDepth {
func (cd *CallDescriptor) getActivationPeriodsForPrefix(key, prefix string, recursionDepth int) (foundPrefix string, aps []*ActivationPeriod, err error) {
if recursionDepth > RECURSION_MAX_DEPTH {
err = errors.New("Max fallback recursion depth reached!" + key)
return
}
rp, err := storageGetter.GetRatingProfile(key)
if rp.FallbackKey != "" {
base = rp.FallbackKey + ":"
key = base + destPrefix
recursionDepth++
return cd.getRatingProfile(key, base, destPrefix, recursionDepth)
if err != nil {
return "", nil, err
}
//get for a smaller prefix if the orignal one was not found
for i := len(cd.Destination); err != nil || fallbackKey != ""; {
if fallbackKey != "" {
base = fallbackKey + ":"
key = base + destPrefix
foundPrefix, aps, err = rp.GetActivationPeriodsForPrefix(prefix)
if err != nil {
if rp.FallbackKey != "" {
recursionDepth++
return cd.getRatingProfile(key, base, destPrefix, recursionDepth)
return cd.getActivationPeriodsForPrefix(rp.FallbackKey, prefix, recursionDepth)
}
i--
if i >= MinPrefixLength {
destPrefix = cd.Destination[:i]
key = base + destPrefix
} else {
break
}
values, fallbackKey, err = storageGetter.GetRatingProfile(key)
}
return
}
@@ -306,7 +259,7 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS
Creates a CallCost structure with the cost information calculated for the received CallDescriptor.
*/
func (cd *CallDescriptor) GetCost() (*CallCost, error) {
destPrefix, err := cd.SearchStorageForPrefix()
destPrefix, err := cd.LoadActivationPeriods()
timespans := cd.splitInTimeSpans()
cost := 0.0
connectionFee := 0.0
@@ -336,7 +289,7 @@ and will decrease it by 10% for nine times. So if the user has little credit it
If the user has no credit then it will return 0.
*/
func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) {
_, err = cd.SearchStorageForPrefix()
_, err = cd.LoadActivationPeriods()
now := time.Now()
availableCredit, availableSeconds := 0.0, 0.0
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {

View File

@@ -57,7 +57,7 @@ func TestSplitSpans(t *testing.T) {
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
timespans := cd.splitInTimeSpans()
if len(timespans) != 2 {
t.Log(cd.ActivationPeriods)
@@ -70,7 +70,7 @@ func TestRedisSplitSpans(t *testing.T) {
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257", TimeStart: t1, TimeEnd: t2}
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
timespans := cd.splitInTimeSpans()
if len(timespans) != 2 {
t.Log(cd.ActivationPeriods)
@@ -215,22 +215,6 @@ func TestMaxSessionTimeNoCredit(t *testing.T) {
}
}
func TestApAddAPIfNotPresent(t *testing.T) {
ap1 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
ap2 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
ap3 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 1, time.UTC)}
cd := &CallDescriptor{}
cd.AddActivationPeriodIfNotPresent(ap1)
cd.AddActivationPeriodIfNotPresent(ap2)
if len(cd.ActivationPeriods) != 1 {
t.Error("Wronfully appended activation period ;)", len(cd.ActivationPeriods))
}
cd.AddActivationPeriodIfNotPresent(ap3)
if len(cd.ActivationPeriods) != 2 {
t.Error("Wronfully not appended activation period ;)", len(cd.ActivationPeriods))
}
}
/*********************************** BENCHMARKS ***************************************/
func BenchmarkStorageGetting(b *testing.B) {
b.StopTimer()
@@ -239,7 +223,7 @@ func BenchmarkStorageGetting(b *testing.B) {
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
storageGetter.GetActivationPeriodsOrFallback(cd.GetKey())
storageGetter.GetRatingProfile(cd.GetKey())
}
}
@@ -250,7 +234,7 @@ func BenchmarkStorageRestoring(b *testing.B) {
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
}
}
@@ -270,7 +254,7 @@ func BenchmarkSplitting(b *testing.B) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd.SearchStorageForPrefix()
cd.LoadActivationPeriods()
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.splitInTimeSpans()

View File

@@ -39,7 +39,7 @@ type CSVReader struct {
rates map[string][]*Rate
timings map[string][]*Timing
activationPeriods map[string]*ActivationPeriod
ratingProfiles map[string]CallDescriptors
ratingProfiles map[string]*RatingProfile
}
func NewFileCSVReader() *CSVReader {
@@ -50,7 +50,7 @@ func NewFileCSVReader() *CSVReader {
c.rates = make(map[string][]*Rate)
c.timings = make(map[string][]*Timing)
c.activationPeriods = make(map[string]*ActivationPeriod)
c.ratingProfiles = make(map[string]CallDescriptors)
c.ratingProfiles = make(map[string]*RatingProfile)
c.readerFunc = openFileCSVReader
return c
}
@@ -98,15 +98,13 @@ func (csvr *CSVReader) WriteToDatabase(storage StorageGetter, flush, verbose boo
if verbose {
log.Print("Rating profiles")
}
for _, cds := range csvr.ratingProfiles {
for _, cd := range cds {
err = storage.SetActivationPeriodsOrFallback(cd.GetKey(), cd.ActivationPeriods, cd.FallbackKey)
if err != nil {
return err
}
if verbose {
log.Print(cd.GetKey())
}
for _, rp := range csvr.ratingProfiles {
err = storage.SetRatingProfile(rp)
if err != nil {
return err
}
if verbose {
log.Print(rp.Id)
}
}
if verbose {
@@ -283,46 +281,24 @@ func (csvr *CSVReader) LoadRatingProfiles(fn string, comma rune) (err error) {
if err != nil {
return errors.New(fmt.Sprintf("Cannot parse activation time from %v", record[6]))
}
key := fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, subject)
rp, ok := csvr.ratingProfiles[key]
if !ok {
rp = &RatingProfile{Id: key}
csvr.ratingProfiles[key] = rp
}
for _, d := range csvr.destinations {
for _, p := range d.Prefixes { //destinations
// Search for a CallDescriptor with the same key
var cd *CallDescriptor
key := fmt.Sprintf("%s:%s:%s:%s:%s", direction, tenant, tor, subject, p)
for _, c := range csvr.ratingProfiles[p] {
if c.GetKey() == key {
cd = c
}
}
if cd == nil {
cd = &CallDescriptor{
Direction: direction,
Tenant: tenant,
TOR: tor,
Subject: subject,
Destination: p,
}
csvr.ratingProfiles[p] = append(csvr.ratingProfiles[p], cd)
}
for _, p := range d.Prefixes { //destinations
ap, exists := csvr.activationPeriods[record[5]]
if !exists {
return errors.New(fmt.Sprintf("Could not load ratinTiming for tag: ", record[5]))
}
newAP := &ActivationPeriod{}
newAP := &ActivationPeriod{ActivationTime: at}
//copy(newAP.Intervals, ap.Intervals)
newAP.Intervals = append(newAP.Intervals, ap.Intervals...)
newAP.ActivationTime = at
cd.AddActivationPeriodIfNotPresent(newAP)
if fallbacksubject != "" &&
csvr.ratingProfiles[p].getKey(fmt.Sprintf("%s:%s:%s:%s:%s", direction, tenant, tor, subject, FallbackDestination)) == nil {
cd = &CallDescriptor{
Direction: direction,
Tenant: tenant,
TOR: tor,
Subject: subject,
Destination: FallbackDestination,
FallbackKey: fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject),
}
csvr.ratingProfiles[p] = append(csvr.ratingProfiles[p], cd)
rp.AddActivationPeriodIfNotPresent(p, newAP)
if fallbacksubject != "" {
rp.FallbackKey = fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fallbacksubject)
}
}
}

View File

@@ -118,17 +118,6 @@ func (rt *RateTiming) GetInterval(r *Rate) (i *Interval) {
return
}
type CallDescriptors []*CallDescriptor
func (cds CallDescriptors) getKey(key string) *CallDescriptor {
for _, cd := range cds {
if cd.GetKey() == key {
return cd
}
}
return nil
}
func ValidateCSVData(fn string, re *regexp.Regexp) (err error) {
fin, err := os.Open(fn)
if err != nil {

View File

@@ -129,7 +129,7 @@ func TestLoadRateTimings(t *testing.T) {
}
func TestLoadRatingProfiles(t *testing.T) {
if len(csvr.ratingProfiles) != 7 {
if len(csvr.ratingProfiles) != 6 {
t.Error("Failed to load rating profiles: ", len(csvr.ratingProfiles), csvr.ratingProfiles)
}
}

View File

@@ -52,7 +52,7 @@ func (d *Destination) containsPrefix(prefix string) (bool, int) {
if d == nil {
return false, 0
}
for i := len(prefix); i >= MinPrefixLength; {
for i := len(prefix); i >= MIN_PREFIX_LENGTH; {
for _, p := range d.Prefixes {
if p == prefix[:i] {
return true, i

105
timespans/ratingprofile.go Normal file
View File

@@ -0,0 +1,105 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012 Radu Ioan Fericean
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <http://www.gnu.org/licenses/>
*/
package timespans
import (
"errors"
"strings"
)
const (
// the minimum length for a destination prefix to be matched.
MIN_PREFIX_LENGTH = 2
)
type RatingProfile struct {
Id string `bson:"_id,omitempty"`
FallbackKey string
DestinationMap map[string][]*ActivationPeriod
}
func (rp *RatingProfile) store() (result string) {
result += rp.FallbackKey + ">"
for k, aps := range rp.DestinationMap {
result += k + "="
for _, ap := range aps {
result += ap.store() + "<"
}
result = strings.TrimRight(result, "<")
result += ">"
}
result = strings.TrimRight(result, ">")
return
}
func (rp *RatingProfile) restore(input string) {
if rp.DestinationMap == nil {
rp.DestinationMap = make(map[string][]*ActivationPeriod, 1)
}
elements := strings.Split(input, ">")
rp.FallbackKey = elements[0]
for _, kv := range elements[1:] {
pair := strings.SplitN(kv, "=", 2)
apList := strings.Split(pair[1], "<")
var newAps []*ActivationPeriod
for _, aps := range apList {
ap := new(ActivationPeriod)
ap.restore(aps)
newAps = append(newAps, ap)
}
rp.DestinationMap[pair[0]] = newAps
}
}
// Adds an activation period that applyes to current rating profile if not already present.
func (rp *RatingProfile) AddActivationPeriodIfNotPresent(destInfo string, aps ...*ActivationPeriod) {
if rp.DestinationMap == nil {
rp.DestinationMap = make(map[string][]*ActivationPeriod, 1)
}
for _, ap := range aps {
found := false
for _, eap := range rp.DestinationMap[destInfo] {
if ap.Equal(eap) {
found = true
break
}
}
if !found {
rp.DestinationMap[destInfo] = append(rp.DestinationMap[destInfo], ap)
}
}
}
func (rp *RatingProfile) GetActivationPeriodsForPrefix(destPrefix string) (foundPrefix string, aps []*ActivationPeriod, err error) {
var found = false
foundPrefix = destPrefix
for i := len(foundPrefix); !found; {
if i >= MIN_PREFIX_LENGTH {
foundPrefix = foundPrefix[:i]
} else {
break
}
aps, found = rp.DestinationMap[foundPrefix]
i--
}
if !found {
return "", nil, errors.New("not found")
}
return
}

View File

@@ -0,0 +1,64 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012 Radu Ioan Fericean
This program is free software: you can Storagetribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
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 <http://www.gnu.org/licenses/>
*/
package timespans
import (
"reflect"
"testing"
"time"
)
func TestRpStoreRestore(t *testing.T) {
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{
Months: Months{time.February},
MonthDays: MonthDays{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
rp := &RatingProfile{FallbackKey: "test"}
rp.AddActivationPeriodIfNotPresent("0723", ap)
result := rp.store()
expected := "test>0723=1328106601000000000|;2;1;3,4;14:30:00;15:00:00;0;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)
}
}
func TestRpAddAPIfNotPresent(t *testing.T) {
ap1 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
ap2 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 0, time.UTC)}
ap3 := &ActivationPeriod{ActivationTime: time.Date(2012, time.July, 2, 14, 24, 30, 1, time.UTC)}
rp := &RatingProfile{}
rp.AddActivationPeriodIfNotPresent("test", ap1)
rp.AddActivationPeriodIfNotPresent("test", ap2)
if len(rp.DestinationMap["test"]) != 1 {
t.Error("Wronfully appended activation period ;)", len(rp.DestinationMap["test"]))
}
rp.AddActivationPeriodIfNotPresent("test", ap3)
if len(rp.DestinationMap["test"]) != 2 {
t.Error("Wronfully not appended activation period ;)", len(rp.DestinationMap["test"]))
}
}

View File

@@ -120,13 +120,13 @@ func (mm *MyMarshaler) Marshal(v interface{}) (data []byte, err error) {
case []*Action:
result := ""
for _, a := range v.([]*Action) {
result += a.store() + "\n"
result += a.store() + "+"
}
return []byte(result), nil
case []*ActionTiming:
result := ""
for _, at := range v.([]*ActionTiming) {
result += at.store() + "\n"
result += at.store() + "+"
}
return []byte(result), nil
case storer:
@@ -144,7 +144,7 @@ func (mm *MyMarshaler) Unmarshal(data []byte, v interface{}) (err error) {
switch v.(type) {
case *[]*Action:
as := v.(*[]*Action)
for _, a_string := range strings.Split(string(data), "\n") {
for _, a_string := range strings.Split(string(data), "+") {
if len(a_string) > 0 {
a := &Action{}
a.restore(a_string)
@@ -154,7 +154,7 @@ func (mm *MyMarshaler) Unmarshal(data []byte, v interface{}) (err error) {
return nil
case *[]*ActionTiming:
ats := v.(*[]*ActionTiming)
for _, at_string := range strings.Split(string(data), "\n") {
for _, at_string := range strings.Split(string(data), "+") {
if len(at_string) > 0 {
at := &ActionTiming{}
at.restore(at_string)

View File

@@ -52,7 +52,7 @@ func (ms *MapStorage) GetRatingProfile(key string) (rp *RatingProfile, err error
}
func (ms *MapStorage) SetRatingProfile(rp *RatingProfile) (err error) {
result, err := ms.ms.Marshal(rp.Id)
result, err := ms.ms.Marshal(rp)
ms.dict[rp.Id] = result
return
}

View File

@@ -71,6 +71,7 @@ func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error
}
return
}
func (rs *RedisStorage) SetDestination(dest *Destination) (err error) {
result, err := rs.ms.Marshal(dest)
return rs.db.Set(dest.Id, result)