switched to json encoding, removed mongo and kyoto and started actions importing

This commit is contained in:
Radu Ioan Fericean
2012-06-13 18:31:28 +03:00
parent 7f5ddf9bf4
commit a7b1d90604
28 changed files with 243 additions and 970 deletions

View File

@@ -25,14 +25,16 @@ import (
)
var (
primaryBalanceActions []*timespans.Action
destinatioBalanceActions []*timespans.Action
actions = make(map[string][]*timespans.Action)
actionsTimings []*timespans.Action
actionsTriggers []*timespans.Action
accountActions []*timespans.Action
)
func loadPrimaryBalanceActions() {
fp, err := os.Open(*primaryBalanceActionsFn)
func loadActions() {
fp, err := os.Open(*actionsFn)
if err != nil {
log.Printf("Could not open primary balance actions file: %v", err)
log.Printf("Could not open actions file: %v", err)
return
}
defer fp.Close()
@@ -45,12 +47,32 @@ func loadPrimaryBalanceActions() {
continue
}
//primaryBalanceActions = append(primaryBalanceActions, record[1:]...)
log.Print(tag, primaryBalanceActions)
log.Print(tag, actions)
}
}
func loadDestinationBalanceActions() {
fp, err := os.Open(*destinationBalanceActionsFn)
func loadActionsTimings() {
fp, err := os.Open(*actionstimingsFn)
if err != nil {
log.Printf("Could not open actions timings file: %v", err)
return
}
defer fp.Close()
csvReader := csv.NewReader(fp)
csvReader.Comma = sep
for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
tag := record[0]
if tag == "Tag" {
// skip header line
continue
}
//destinatioBalanceActions = append(destinatioBalanceActions, record[1:]...)
log.Print(tag, actionsTimings)
}
}
func loadActionsTriggers() {
fp, err := os.Open(*actionstriggersFn)
if err != nil {
log.Printf("Could not open destination balance actions file: %v", err)
return
@@ -65,6 +87,26 @@ func loadDestinationBalanceActions() {
continue
}
//destinatioBalanceActions = append(destinatioBalanceActions, record[1:]...)
log.Print(tag, destinatioBalanceActions)
log.Print(tag, actionsTriggers)
}
}
func loadAccountActions() {
fp, err := os.Open(*accountactionsFn)
if err != nil {
log.Printf("Could not open account actions file: %v", err)
return
}
defer fp.Close()
csvReader := csv.NewReader(fp)
csvReader.Comma = sep
for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
tag := record[0]
if tag == "Tag" {
// skip header line
continue
}
//destinatioBalanceActions = append(destinatioBalanceActions, record[1:]...)
log.Print(tag, accountActions)
}
}

View File

@@ -24,22 +24,24 @@ import (
)
var (
separator = flag.String("separator", ",", "Default field separator")
redisserver = flag.String("redisserver", "tcp:127.0.0.1:6379", "redis server address (tcp:127.0.0.1:6379)")
redisdb = flag.Int("rdb", 10, "redis database number (10)")
redispass = flag.String("pass", "", "redis database password")
flush = flag.Bool("flush", false, "Flush the database before importing")
monthsFn = flag.String("month", "Months.csv", "Months file")
monthdaysFn = flag.String("monthdays", "MonthDays.csv", "Month days file")
weekdaysFn = flag.String("weekdays", "WeekDays.csv", "Week days file")
destinationsFn = flag.String("destinations", "Destinations.csv", "Destinations file")
ratesFn = flag.String("rates", "Rates.csv", "Rates file")
timingsFn = flag.String("timings", "Timings.csv", "Timings file")
ratestimingsFn = flag.String("ratestimings", "RatesTimings.csv", "Rates timings file")
ratingprofilesFn = flag.String("ratingprofiles", "RatingProfiles.csv", "Rating profiles file")
primaryBalanceActionsFn = flag.String("primaryBalanceActions", "PrimaryBalanceActions.csv", "Primary balance actions file")
destinationBalanceActionsFn = flag.String("destinationBalanceActions", "DestinationBalanceActions.csv", "Destination balance actions file")
sep rune
separator = flag.String("separator", ",", "Default field separator")
redisserver = flag.String("redisserver", "tcp:127.0.0.1:6379", "redis server address (tcp:127.0.0.1:6379)")
redisdb = flag.Int("rdb", 10, "redis database number (10)")
redispass = flag.String("pass", "", "redis database password")
flush = flag.Bool("flush", false, "Flush the database before importing")
monthsFn = flag.String("month", "Months.csv", "Months file")
monthdaysFn = flag.String("monthdays", "MonthDays.csv", "Month days file")
weekdaysFn = flag.String("weekdays", "WeekDays.csv", "Week days file")
destinationsFn = flag.String("destinations", "Destinations.csv", "Destinations file")
ratesFn = flag.String("rates", "Rates.csv", "Rates file")
timingsFn = flag.String("timings", "Timings.csv", "Timings file")
ratestimingsFn = flag.String("ratestimings", "RatesTimings.csv", "Rates timings file")
ratingprofilesFn = flag.String("ratingprofiles", "RatingProfiles.csv", "Rating profiles file")
actionsFn = flag.String("actions", "Actions.csv", "Actions file")
actionstimingsFn = flag.String("actionstimings", "ActionsTimings.csv", "Actions timings file")
actionstriggersFn = flag.String("actionstriggers", "ActionsTriggers.csv", "Actions triggers file")
accountactionsFn = flag.String("accountactions", "AccountActions.csv", "Account actions file")
sep rune
)
func writeToDatabase() {
@@ -70,7 +72,9 @@ func main() {
loadTimings()
loadRatesTimings()
loadRatingProfiles()
loadPrimaryBalanceActions()
loadDestinationBalanceActions()
loadActions()
loadActionsTimings()
loadActionsTriggers()
loadAccountActions()
writeToDatabase()
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/cgrates/cgrates/timespans"
"log"
"strconv"
"time"
)
type Rate struct {
@@ -68,12 +69,15 @@ type Timing struct {
}
func NewTiming(timeingInfo ...string) (rt *Timing) {
rt = &Timing{
StartTime: timeingInfo[3],
}
rt = &Timing{}
rt.Months.Parse(timeingInfo[0], ";")
rt.MonthDays.Parse(timeingInfo[1], ";")
rt.WeekDays.Parse(timeingInfo[2], ";")
if timeingInfo[3] == "*now" {
rt.StartTime = time.Now().Format("00:00:00")
} else {
rt.StartTime = timeingInfo[3]
}
return
}

3
data/AccountActions.csv Normal file
View File

@@ -0,0 +1,3 @@
Tenant,Account,Direction,ActionTimingsTag,ActionTriggersTag
CUSTOMER_1,rif,OUT,STANDARD_ABO,STANDARD_TRIGGER
CUSTOMER_1,dan,OUT,STANDARD_ABO,STANDARD_TRIGGER
1 Tenant Account Direction ActionTimingsTag ActionTriggersTag
2 CUSTOMER_1 rif OUT STANDARD_ABO STANDARD_TRIGGER
3 CUSTOMER_1 dan OUT STANDARD_ABO STANDARD_TRIGGER

6
data/ActionTimings.csv Normal file
View File

@@ -0,0 +1,6 @@
Tag,ActionsTag,TimingTag
STANDARD_ABO,SOME,WEEKLY_SAME_TIME
STANDARD_ABO,SOME,WEEKLY_SAME_TIME
STANDARD_ABO,SOME,WEEKLY_SAME_TIME
STANDARD_ABO,SOME,ONE_TIME_RUN
STANDARD_ABO,SOME,FIRST_DAY_OF_MONTH
1 Tag ActionsTag TimingTag
2 STANDARD_ABO SOME WEEKLY_SAME_TIME
3 STANDARD_ABO SOME WEEKLY_SAME_TIME
4 STANDARD_ABO SOME WEEKLY_SAME_TIME
5 STANDARD_ABO SOME ONE_TIME_RUN
6 STANDARD_ABO SOME FIRST_DAY_OF_MONTH

5
data/ActionTriggers.csv Normal file
View File

@@ -0,0 +1,5 @@
ActionTag,BalanceTag,ThresholdValue,DestinationTag,ActionsTag
STANDARD_TRIGGER,MONETARY,30,*all,SOME_1
STANDARD_TRIGGER,SMS,30,*all,SOME_2
STANDARD_TRIGGER,MINUTES,10,GERMANY_O2,SOME_1
STANDARD_TRIGGER,MINUTES,200,GERMANY,SOME_2
1 ActionTag BalanceTag ThresholdValue DestinationTag ActionsTag
2 STANDARD_TRIGGER MONETARY 30 *all SOME_1
3 STANDARD_TRIGGER SMS 30 *all SOME_2
4 STANDARD_TRIGGER MINUTES 10 GERMANY_O2 SOME_1
5 STANDARD_TRIGGER MINUTES 200 GERMANY SOME_2

8
data/Actions.csv Normal file
View File

@@ -0,0 +1,8 @@
Tag,Action,BalanceTag,Units,DestinationTag,PriceType,PriceValue,Weight
SOME,TOPUP_RESET,MONETARY,10,*all,,,
SOME,TOPUP_RESET,SMS,100,*all,,,
SOME,TOPUP_RESET,INTERNET,1000,*all,,,
SOME,POSTPAID_RESET,MONETARY,10,*all,,,
SOME,DEBIT,MONETARY,5,*all,,,
SOME_1,DEBIT,MINUTES,10,GERMANY_O2,PERCENT,25,10
SOME_2,TOPUP_RESET,MINUTES,1000,GERMANY,ABSOLUTE,0.2,10
1 Tag Action BalanceTag Units DestinationTag PriceType PriceValue Weight
2 SOME TOPUP_RESET MONETARY 10 *all
3 SOME TOPUP_RESET SMS 100 *all
4 SOME TOPUP_RESET INTERNET 1000 *all
5 SOME POSTPAID_RESET MONETARY 10 *all
6 SOME DEBIT MONETARY 5 *all
7 SOME_1 DEBIT MINUTES 10 GERMANY_O2 PERCENT 25 10
8 SOME_2 TOPUP_RESET MINUTES 1000 GERMANY ABSOLUTE 0.2 10

View File

@@ -1,4 +0,0 @@
TimingTag,Action,Tenant,Account,Direction,DestinationTag,Units,PriceType,PriceValue,Weight
WEEKLY_SAME_TIME,TOPUP_RESET,CUSTOMER_1,rif,OUT,GERMANY,1000,PRECENT,10,10
WEEKLY_SAME_TIME,TOPUP_RESET,CUSTOMER_1,rif,OUT,GERMANY_O2,100,ABSOLUTE,0,10
TRIGGERED,TOPUP_ADD,CUSTOMER_1,rif,OUT,GERMANY_O2,50,THRESHOLD,300,10
1 TimingTag Action Tenant Account Direction DestinationTag Units PriceType PriceValue Weight
2 WEEKLY_SAME_TIME TOPUP_RESET CUSTOMER_1 rif OUT GERMANY 1000 PRECENT 10 10
3 WEEKLY_SAME_TIME TOPUP_RESET CUSTOMER_1 rif OUT GERMANY_O2 100 ABSOLUTE 0 10
4 TRIGGERED TOPUP_ADD CUSTOMER_1 rif OUT GERMANY_O2 50 THRESHOLD 300 10

View File

@@ -1,6 +0,0 @@
TimingTag,Action,Tenant,Account,Direction,BalanceTag,Units
WEEKLY_SAME_TIME,TOPUP_RESET,CUSTOMER_1,rif,OUT,MONETARY,10
WEEKLY_SAME_TIME,TOPUP_RESET,CUSTOMER_1,rif,OUT,SMS,100
WEEKLY_SAME_TIME,TOPUP_RESET,CUSTOMER_1,rif,OUT,INTERNET,1000
ONE_TIME_RUN,POSTPAID_RESET,CUSTOMER_1,dan,OUT,MONETARY,10
FIRST_DAY_OF_MONTH,DEBIT,CUSTOMER_1,dan,OUT,MONETARY,5
1 TimingTag Action Tenant Account Direction BalanceTag Units
2 WEEKLY_SAME_TIME TOPUP_RESET CUSTOMER_1 rif OUT MONETARY 10
3 WEEKLY_SAME_TIME TOPUP_RESET CUSTOMER_1 rif OUT SMS 100
4 WEEKLY_SAME_TIME TOPUP_RESET CUSTOMER_1 rif OUT INTERNET 1000
5 ONE_TIME_RUN POSTPAID_RESET CUSTOMER_1 dan OUT MONETARY 10
6 FIRST_DAY_OF_MONTH DEBIT CUSTOMER_1 dan OUT MONETARY 5

View File

@@ -1,9 +1,9 @@
TimingTag,MonthsTag,MonthDaysTag,WeekDaysTag,StartTime
WORKDAYS_00,ALL,ALL,WORKDAYS,00:00:00
WORKDAYS_18,ALL,ALL,WORKDAYS,18:00:00
WEEKENDS,ALL,ALL,WEEKENDS,00:00:00
WEEKLY_SAME_TIME,ALL,ALL,MONDAY,*now
FIRST_DAY_OF_MONTH,ALL,FIRST,ALL,00:00:00
DAILY_SAME_TIME,ALL,ALL,ALL,*now
ONE_TIME_RUN,NONE,NONE,NONE,*now
TRIGGERED,NONE,NONE,NONE,*trigerred
TimingTag,Months,MonthDays,WeekDays,StartTime
WORKDAYS_00,*all,*all,*all,00:00:00
WORKDAYS_18,*all,*all,*all,18:00:00
WEEKENDS,*all,*all,6;7,00:00:00
WEEKLY_SAME_TIME,*all,*all,1,*now
FIRST_DAY_OF_MONTH,*all,1,*all,00:00:00
DAILY_SAME_TIME,*all,*all,*all,*now
ONE_TIME_RUN,*none,*none,*none,*now
WINTER_FIRST_DAYS,1;2;12,1,*none,00:00:00
1 TimingTag MonthsTag Months MonthDaysTag MonthDays WeekDaysTag WeekDays StartTime
2 WORKDAYS_00 ALL *all ALL *all WORKDAYS *all 00:00:00
3 WORKDAYS_18 ALL *all ALL *all WORKDAYS *all 18:00:00
4 WEEKENDS ALL *all ALL *all WEEKENDS 6;7 00:00:00
5 WEEKLY_SAME_TIME ALL *all ALL *all MONDAY 1 *now
6 FIRST_DAY_OF_MONTH ALL *all FIRST 1 ALL *all 00:00:00
7 DAILY_SAME_TIME ALL *all ALL *all ALL *all *now
8 ONE_TIME_RUN NONE *none NONE *none NONE *none *now
9 TRIGGERED WINTER_FIRST_DAYS NONE 1;2;12 NONE 1 NONE *none *trigerred 00:00:00

Binary file not shown.

View File

@@ -18,10 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package timespans
import (
"bytes"
"encoding/gob"
)
import ()
// Amount of a trafic of a certain type (TOR)
type UnitsCounter struct {
@@ -62,32 +59,18 @@ func (uc *UnitsCounter) getDestination() (dest *Destination) {
Structure to be filled for each tariff plan with the bonus value for received calls minutes.
*/
type Action struct {
Id string
ActionType string
Direction string
TOR string
Units float64
balanceMap map[string]float64
BalanceMap map[string]float64
MinuteBuckets []*MinuteBucket
Weight float64
DestinationsId string
destination *Destination
}
/*
Serializes the tariff plan for the storage. Used for key-value storages.
*/
func (a *Action) store() (result string) {
buf := new(bytes.Buffer)
gob.NewEncoder(buf).Encode(a)
return buf.String()
}
/*
De-serializes the tariff plan for the storage. Used for key-value storages.
*/
func (a *Action) restore(input string) {
gob.NewDecoder(bytes.NewBuffer([]byte(input))).Decode(a)
}
// Structure to store actions according to weight
type actionsorter []*Action
@@ -102,3 +85,18 @@ func (s actionsorter) Swap(i, j int) {
func (s actionsorter) Less(j, i int) bool {
return s[i].Weight < s[j].Weight
}
type ActionTrigger struct {
BalanceId string
ThresholdValue float64
DestinationId string
destination *Destination
ActionsId string
actions []*Action
}
type ActionTiming struct {
Id string
ActionsId string
actions []*Action
}

View File

@@ -21,8 +21,6 @@ package timespans
import (
"time"
//"log"
"strconv"
"strings"
)
/*
@@ -41,49 +39,3 @@ func (ap *ActivationPeriod) AddInterval(is ...*Interval) {
ap.Intervals = append(ap.Intervals, i)
}
}
/*
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 = i.Months.store() + "|"
is += i.MonthDays.store() + "|"
is += i.WeekDays.store() + "|"
is += i.StartTime + "|"
is += i.EndTime + "|"
is += strconv.FormatFloat(i.Weight, '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, "|")
i.Months.restore(ise[0])
i.MonthDays.restore(ise[1])
i.WeekDays.restore(ise[2])
i.StartTime = ise[3]
i.EndTime = ise[4]
i.Weight, _ = 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)
}
}

View File

@@ -21,6 +21,7 @@ package timespans
import (
"reflect"
"testing"
"encoding/json"
"time"
//"log"
)
@@ -30,16 +31,6 @@ func init() {
SetStorageGetter(sg)
}
func TestApRestoreKyoto(t *testing.T) {
cd := &CallDescriptor{Tenant: "vdf",
Subject: "rif",
Destination: "0257"}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 2 {
t.Error("Error restoring activation periods: ", cd.ActivationPeriods)
}
}
func TestApRestoreRedis(t *testing.T) {
cd := &CallDescriptor{Tenant: "vdf",
Subject: "rif",
@@ -59,13 +50,13 @@ func TestApStoreRestore(t *testing.T) {
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
result := ap.store()
expected := "1328106601000000000;2|1|3,4|14:30:00|15:00:00|0|0|0|0;"
if result != expected {
result, _ := json.Marshal(ap)
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Months\":[2],\"MonthDays\":[1],\"WeekDays\":[3,4],\"StartTime\":\"14:30:00\",\"EndTime\":\"15:00:00\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"BillingUnit\":0}]}"
if string(result) != expected {
t.Errorf("Expected %q was %q", expected, result)
}
ap1 := ActivationPeriod{}
ap1.restore(result)
json.Unmarshal(result, &ap1)
if reflect.DeepEqual(ap, ap1) {
t.Errorf("Expected %v was %v", ap, ap1)
}
@@ -76,22 +67,19 @@ func TestApStoreRestoreBlank(t *testing.T) {
i := &Interval{}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
result := ap.store()
expected := "1328106601000000000;|||||0|0|0|0;"
if result != expected {
result, _ := json.Marshal(ap)
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"BillingUnit\":0}]}"
if string(result) != expected {
t.Errorf("Expected %q was %q", expected, result)
}
ap1 := ActivationPeriod{}
ap1.restore(result)
json.Unmarshal(result, &ap1)
if reflect.DeepEqual(ap, ap1) {
t.Errorf("Expected %v was %v", ap, ap1)
}
}
func TestFallbackDirect(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{Tenant: "vdf", Subject: "rif", Destination: "0745"}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 1 {
@@ -100,9 +88,6 @@ func TestFallbackDirect(t *testing.T) {
}
func TestFallbackWithBackTrace(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{Tenant: "vdf", Subject: "rif", Destination: "0745121"}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 1 {
@@ -111,9 +96,6 @@ func TestFallbackWithBackTrace(t *testing.T) {
}
func TestFallbackDefault(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{Tenant: "vdf", Subject: "rif", Destination: "00000"}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 1 {
@@ -122,9 +104,6 @@ func TestFallbackDefault(t *testing.T) {
}
func TestFallbackNoInfiniteLoop(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{Tenant: "vdf", Subject: "rif", Destination: "0721"}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 0 {
@@ -137,7 +116,7 @@ func TestFallbackNoInfiniteLoop(t *testing.T) {
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;")
json.Unmarshal([]byte("1328106601;2|1|3,4|14:30:00|15:00:00|0|0|0|0;"), &ap)
}
}
@@ -155,7 +134,7 @@ func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
ap1 := ActivationPeriod{}
b.StartTimer()
for i := 0; i < b.N; i++ {
result := ap.store()
ap1.restore(result)
result, _ := json.Marshal(ap)
json.Unmarshal(result, &ap1)
}
}

View File

@@ -165,7 +165,6 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS
timespans = append(timespans, firstSpan)
// split on (free) minute buckets
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
userBalance.mux.RLock()
_, bucketList := userBalance.getSecondsForPrefix(cd.Destination)
for _, mb := range bucketList {
for i := 0; i < len(timespans); i++ {
@@ -180,7 +179,6 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS
}
}
}
userBalance.mux.RUnlock()
}
if firstSpan.MinuteInfo != nil {
return // all the timespans are on minutes
@@ -285,10 +283,8 @@ func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) {
if userBalance.Type == UB_TYPE_POSTPAID {
return -1, nil
} else {
userBalance.mux.RLock()
availableCredit = userBalance.BalanceMap[CREDIT]
availableSeconds, _ = userBalance.getSecondsForPrefix(cd.Destination)
userBalance.mux.RUnlock()
}
} else {
return cd.Amount, err
@@ -389,9 +385,9 @@ The amount filed has to be filled in call descriptor.
/*
Resets user balances value to the amounts specified in the tariff plan.
*/
func (cd *CallDescriptor) ResetUserBalance() (err error) {
/*func (cd *CallDescriptor) ResetUserBalance() (err error) {
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
return userBalance.resetUserBalance()
}
return err
}
}*/

View File

@@ -80,26 +80,6 @@ func TestKyotoGetCost(t *testing.T) {
}
func TestRedisGetCost(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
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{Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 540, ConnectFee: 0}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
t.Errorf("Expected %v was %v", expected, result)
}
}
func TestMongoGetCost(t *testing.T) {
getter, err := NewMongoStorage("127.0.0.1", "test")
if err != nil {
return
}
defer getter.Close()
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{Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
@@ -111,8 +91,6 @@ func TestMongoGetCost(t *testing.T) {
}
func TestFullDestNotFound(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
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)
@@ -126,9 +104,6 @@ func TestFullDestNotFound(t *testing.T) {
}
func TestMultipleActivationPeriods(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
t1 := time.Date(2012, time.February, 8, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
@@ -301,39 +276,8 @@ func BenchmarkRedisGetCost(b *testing.B) {
}
}
func BenchmarkKyotoGetting(b *testing.B) {
b.StopTimer()
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
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{Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
key := cd.GetKey()
getter.GetActivationPeriodsOrFallback(key)
}
}
func BenchmarkKyotoRestoring(b *testing.B) {
b.StopTimer()
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
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{Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.SearchStorageForPrefix()
}
}
func BenchmarkSplitting(b *testing.B) {
b.StopTimer()
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
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)
@@ -345,70 +289,6 @@ func BenchmarkSplitting(b *testing.B) {
}
}
func BenchmarkKyotoGetCost(b *testing.B) {
b.StopTimer()
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
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{Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetCost()
}
}
func BenchmarkMongoGetting(b *testing.B) {
b.StopTimer()
getter, _ := NewMongoStorage("127.0.0.1", "test")
defer getter.Close()
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{Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
getter.GetActivationPeriodsOrFallback(cd.GetKey())
}
}
func BenchmarkMongoGetCost(b *testing.B) {
b.StopTimer()
getter, _ := NewMongoStorage("127.0.0.1", "test")
defer getter.Close()
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{Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetCost()
}
}
func BenchmarkKyotoSingleGetSessionTime(b *testing.B) {
b.StopTimer()
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
cd := &CallDescriptor{Tenant: "vdf", Subject: "minutosu", Destination: "0723", Amount: 100}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetMaxSessionTime()
}
}
func BenchmarkKyotoMultipleGetSessionTime(b *testing.B) {
b.StopTimer()
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
cd := &CallDescriptor{Tenant: "vdf", Subject: "minutosu", Destination: "0723", Amount: 5400}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetMaxSessionTime()
}
}
func BenchmarkRedisSingleGetSessionTime(b *testing.B) {
b.StopTimer()
getter, _ := NewRedisStorage("", 10)
@@ -430,25 +310,3 @@ func BenchmarkRedisMultipleGetSessionTime(b *testing.B) {
cd.GetMaxSessionTime()
}
}
func BenchmarkMongoSingleGetSessionTime(b *testing.B) {
b.StopTimer()
getter, _ := NewMongoStorage("127.0.0.1", "test")
defer getter.Close()
cd := &CallDescriptor{Tenant: "vdf", Subject: "minutosu", Destination: "0723", Amount: 100}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetMaxSessionTime()
}
}
func BenchmarkMongoMultipleGetSessionTime(b *testing.B) {
b.StopTimer()
getter, _ := NewMongoStorage("127.0.0.1", "test")
defer getter.Close()
cd := &CallDescriptor{Tenant: "vdf", Subject: "minutosu", Destination: "0723", Amount: 5400}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetMaxSessionTime()
}
}

View File

@@ -40,30 +40,20 @@ func (m Months) Contains(month time.Month) (result bool) {
return
}
/*
Serializes the month for the storage. Used for key-value storages.
*/
func (m Months) store() (result string) {
for _, ms := range m {
result += strconv.Itoa(int(ms)) + ","
}
result = strings.TrimRight(result, ",")
return
}
/*
De-serializes the month for the storage. Used for key-value storages.
*/
func (m *Months) restore(input string) {
m.Parse(input, ",")
}
// Loades Month elemnents from a string separated by sep.
func (m *Months) Parse(input, sep string) {
elements := strings.Split(input, sep)
for _, ms := range elements {
if month, err := strconv.Atoi(ms); err == nil {
*m = append(*m, time.Month(month))
switch input {
case "*all":
*m = []time.Month{time.January, time.February, time.March, time.April, time.May, time.June,
time.July, time.August, time.September, time.October, time.November, time.December}
case "*none":
*m = []time.Month{}
default:
elements := strings.Split(input, sep)
for _, ms := range elements {
if month, err := strconv.Atoi(ms); err == nil {
*m = append(*m, time.Month(month))
}
}
}
}
@@ -83,30 +73,19 @@ func (md MonthDays) Contains(monthDay int) (result bool) {
return
}
/*
Serializes the month days for the storage. Used for key-value storages.
*/
func (md MonthDays) store() (result string) {
for _, mds := range md {
result += strconv.Itoa(mds) + ","
}
result = strings.TrimRight(result, ",")
return
}
/*
De-serializes the month days for the storage. Used for key-value storages.
*/
func (md *MonthDays) restore(input string) {
md.Parse(input, ",")
}
// Parse MonthDay elements from string separated by sep.
func (md *MonthDays) Parse(input, sep string) {
elements := strings.Split(input, sep)
for _, mds := range elements {
if day, err := strconv.Atoi(mds); err == nil {
*md = append(*md, day)
switch input {
case "*all":
*md = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
case "*none":
*md = []int{}
default:
elements := strings.Split(input, sep)
for _, mds := range elements {
if day, err := strconv.Atoi(mds); err == nil {
*md = append(*md, day)
}
}
}
}
@@ -126,29 +105,18 @@ func (wd WeekDays) Contains(weekDay time.Weekday) (result bool) {
return
}
/*
Serializes the week days for the storage. Used for key-value storages.
*/
func (wd WeekDays) store() (result string) {
for _, wds := range wd {
result += strconv.Itoa(int(wds)) + ","
}
result = strings.TrimRight(result, ",")
return
}
/*
De-serializes the week days for the storage. Used for key-value storages.
*/
func (wd *WeekDays) restore(input string) {
wd.Parse(input, ",")
}
func (wd *WeekDays) Parse(input, sep string) {
elements := strings.Split(input, sep)
for _, wds := range elements {
if day, err := strconv.Atoi(wds); err == nil {
*wd = append(*wd, time.Weekday(day))
switch input {
case "*all":
*wd = []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday, time.Saturday, time.Sunday}
case "*none":
*wd = []time.Weekday{}
default:
elements := strings.Split(input, sep)
for _, wds := range elements {
if day, err := strconv.Atoi(wds); err == nil {
*wd = append(*wd, time.Weekday(day%7)) // %7 for sunday = 7 normalization
}
}
}
}

View File

@@ -22,16 +22,17 @@ import (
"reflect"
"testing"
"time"
"encoding/json"
)
func TestMonthStoreRestore(t *testing.T) {
m := Months{5, 6, 7, 8}
r := m.store()
if r != "5,6,7,8," {
r, _ := json.Marshal(m)
if string(r) != "5,6,7,8," {
t.Errorf("Error serializing months: %v", r)
}
o := Months{}
o.restore(r)
json.Unmarshal(r, &o)
if !reflect.DeepEqual(o, m) {
t.Errorf("Expected %v was %v", m, o)
}
@@ -39,12 +40,12 @@ func TestMonthStoreRestore(t *testing.T) {
func TestMonthDayStoreRestore(t *testing.T) {
md := MonthDays{24, 25, 26}
r := md.store()
if r != "24,25,26," {
r, _ := json.Marshal(md)
if string(r) != "24,25,26," {
t.Errorf("Error serializing month days: %v", r)
}
o := MonthDays{}
o.restore(r)
json.Unmarshal(r, &o)
if !reflect.DeepEqual(o, md) {
t.Errorf("Expected %v was %v", md, o)
}
@@ -52,12 +53,12 @@ func TestMonthDayStoreRestore(t *testing.T) {
func TestWeekDayStoreRestore(t *testing.T) {
wd := WeekDays{time.Saturday, time.Sunday}
r := wd.store()
if r != "6,0," {
r, _ := json.Marshal(wd)
if string(r) != "6,0," {
t.Errorf("Error serializing week days: %v", r)
}
o := WeekDays{}
o.restore(r)
json.Unmarshal(r, &o)
if !reflect.DeepEqual(o, wd) {
t.Errorf("Expected %v was %v", wd, o)
}

View File

@@ -18,10 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package timespans
import (
"strings"
)
/*
Structure that gathers multiple destination prefixes under a common id.
*/
@@ -30,21 +26,6 @@ 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.
*/

View File

@@ -21,60 +21,35 @@ package timespans
import (
"reflect"
"testing"
"encoding/json"
)
func init() {
sg, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
SetStorageGetter(sg)
}
func TestDestinationStoreRestore(t *testing.T) {
nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
s := nationale.store()
s, _ := json.Marshal(nationale)
d1 := &Destination{Id: "nat"}
d1.restore(s)
if d1.store() != s {
t.Errorf("Expected %q was %q", s, d1.store())
}
}
func TestDestinationKyotoStore(t *testing.T) {
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
getter.SetDestination(nationale)
result, _ := getter.GetDestination(nationale.Id)
if !reflect.DeepEqual(nationale, result) {
t.Errorf("Expected %q was %q", nationale, result)
json.Unmarshal(s, d1)
s1, _ := json.Marshal(d1)
if reflect.DeepEqual(s1, s) {
t.Errorf("Expected %q was %q", s, s1)
}
}
func TestDestinationRedisStore(t *testing.T) {
getter, err := NewRedisStorage("tcp:127.0.0.1:6379", 10)
if err != nil {
return
}
defer getter.Close()
nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
getter.SetDestination(nationale)
result, _ := getter.GetDestination(nationale.Id)
if !reflect.DeepEqual(nationale, result) {
t.Errorf("Expected %q was %q", nationale, result)
}
}
func TestDestinationMongoStore(t *testing.T) {
getter, err := NewMongoStorage("127.0.0.1", "test")
if err != nil {
return
}
defer getter.Close()
nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
getter.SetDestination(nationale)
result, _ := getter.GetDestination(nationale.Id)
storageGetter.SetDestination(nationale)
result, _ := storageGetter.GetDestination(nationale.Id)
if !reflect.DeepEqual(nationale, result) {
t.Errorf("Expected %q was %q", nationale, result)
}
}
func TestDestinationContainsPrefix(t *testing.T) {
getter, _ := NewMongoStorage("127.0.0.1", "test")
defer getter.Close()
nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
contains, precision := nationale.containsPrefix("0256")
if !contains || precision != len("0256") {
@@ -85,32 +60,10 @@ func TestDestinationContainsPrefix(t *testing.T) {
/********************************* Benchmarks **********************************/
func BenchmarkDestinationKyotoStoreRestore(b *testing.B) {
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
for i := 0; i < b.N; i++ {
getter.SetDestination(nationale)
getter.GetDestination(nationale.Id)
}
}
func BenchmarkDestinationRedisStoreRestore(b *testing.B) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
for i := 0; i < b.N; i++ {
getter.SetDestination(nationale)
getter.GetDestination(nationale.Id)
}
}
func BenchmarkDestinationMongoStoreRestore(b *testing.B) {
getter, _ := NewMongoStorage("127.0.0.1", "test")
defer getter.Close()
nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
for i := 0; i < b.N; i++ {
getter.SetDestination(nationale)
getter.GetDestination(nationale.Id)
storageGetter.SetDestination(nationale)
storageGetter.GetDestination(nationale.Id)
}
}

View File

@@ -21,8 +21,6 @@ package timespans
import (
// "log"
"math"
"strconv"
"strings"
)
type MinuteBucket struct {
@@ -35,28 +33,6 @@ 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.FormatFloat(mb.Weight, 'f', -1, 64) + "|"
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.Weight, _ = strconv.ParseFloat(elements[1], 64)
mb.Price, _ = strconv.ParseFloat(elements[2], 64)
mb.DestinationId = elements[3]
}
/*
Returns the destination loading it from the storage if necessary.
*/

View File

@@ -28,8 +28,8 @@ type StorageGetter interface {
SetActivationPeriodsOrFallback(string, []*ActivationPeriod, string) error
GetDestination(string) (*Destination, error)
SetDestination(*Destination) error
GetTariffPlan(string) (*TariffPlan, error)
SetTariffPlan(*TariffPlan) error
GetActions(string) ([]*Action, error)
SetActions(string, []*Action) error
GetUserBalance(string) (*UserBalance, error)
SetUserBalance(*UserBalance) error
}

View File

@@ -1,110 +0,0 @@
/*
Rating system designed to be used in VoIP Carrieks 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 veksion 3 of the License, or
(at your option) any later veksion.
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 (
//"github.com/fsouza/gokabinet/kc"
"bitbucket.org/ww/cabinet"
//"log"
"strings"
)
type KyotoStorage struct {
db *cabinet.KCDB
}
func NewKyotoStorage(filaName string) (*KyotoStorage, error) {
// ndb, err := kc.Open(filaName, kc.WRITE)
ndb := cabinet.New()
err := ndb.Open(filaName, cabinet.KCOWRITER|cabinet.KCOCREATE)
return &KyotoStorage{db: ndb}, err
}
func (ks *KyotoStorage) Close() {
ks.db.Close()
}
func (ks *KyotoStorage) GetActivationPeriodsOrFallback(key string) (aps []*ActivationPeriod, fallbackKey string, err error) {
valuesBytes, err := ks.db.Get([]byte(key))
if err != nil {
return
}
valuesString := string(valuesBytes)
values := strings.Split(valuesString, "\n")
if len(values) > 1 {
for _, ap_string := range values {
if len(ap_string) > 0 {
ap := &ActivationPeriod{}
ap.restore(ap_string)
aps = append(aps, ap)
}
}
} else { // fallback case
fallbackKey = valuesString
}
return
}
func (ks *KyotoStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) error {
result := ""
if len(aps) > 0 {
for _, ap := range aps {
result += ap.store() + "\n"
}
} else {
result = fallbackKey
}
return ks.db.Set([]byte(key), []byte(result))
}
func (ks *KyotoStorage) GetDestination(key string) (dest *Destination, err error) {
if values, err := ks.db.Get([]byte(key)); err == nil {
dest = &Destination{Id: key}
dest.restore(string(values))
}
return
}
func (ks *KyotoStorage) SetDestination(dest *Destination) error {
return ks.db.Set([]byte(dest.Id), []byte(dest.store()))
}
func (ks *KyotoStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
if values, err := ks.db.Get([]byte(key)); err == nil {
tp = &TariffPlan{Id: key}
tp.restore(string(values))
}
return
}
func (ks *KyotoStorage) SetTariffPlan(tp *TariffPlan) error {
return ks.db.Set([]byte(tp.Id), []byte(tp.store()))
}
func (ks *KyotoStorage) GetUserBalance(key string) (ub *UserBalance, err error) {
if values, err := ks.db.Get([]byte(key)); err == nil {
ub = &UserBalance{Id: key}
ub.restore(string(values))
}
return
}
func (ks *KyotoStorage) SetUserBalance(ub *UserBalance) error {
return ks.db.Set([]byte(ub.Id), []byte(ub.store()))
}

View File

@@ -1,109 +0,0 @@
/*
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 (
"launchpad.net/mgo"
"launchpad.net/mgo/bson"
"log"
)
type MongoStorage struct {
db *mgo.Database
session *mgo.Session
}
func NewMongoStorage(address, db string) (*MongoStorage, error) {
session, err := mgo.Dial(address)
if err != nil {
log.Print("Could not contact mongo server")
return nil, err
}
session.SetMode(mgo.Monotonic, true)
index := mgo.Index{Key: []string{"key"}, Unique: true, DropDups: true, Background: true}
err = session.DB(db).C("activationPeriods").EnsureIndex(index)
index = mgo.Index{Key: []string{"id"}, Unique: true, DropDups: true, Background: true}
err = session.DB(db).C("destinations").EnsureIndex(index)
err = session.DB(db).C("tariffPlans").EnsureIndex(index)
err = session.DB(db).C("userBalance").EnsureIndex(index)
return &MongoStorage{db: session.DB(db), session: session}, nil
}
func (ms *MongoStorage) Close() {
ms.session.Close()
}
/*
Helper type for activation periods storage.
*/
type KeyValue struct {
Key string
FallbackKey string
ActivationPeriods []*ActivationPeriod
}
func (ms *MongoStorage) GetActivationPeriodsOrFallback(key string) (aps []*ActivationPeriod, fallbackKey string, err error) {
ndb := ms.db.C("activationPeriods")
result := KeyValue{}
err = ndb.Find(bson.M{"key": key}).One(&result)
return result.ActivationPeriods, result.FallbackKey, err
}
func (ms *MongoStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) error {
ndb := ms.db.C("activationPeriods")
return ndb.Insert(&KeyValue{key, fallbackKey, aps})
}
func (ms *MongoStorage) GetDestination(key string) (result *Destination, err error) {
ndb := ms.db.C("destinations")
result = &Destination{}
err = ndb.Find(bson.M{"id": key}).One(result)
return
}
func (ms *MongoStorage) SetDestination(dest *Destination) error {
ndb := ms.db.C("destinations")
return ndb.Insert(&dest)
}
func (ms *MongoStorage) GetTariffPlan(key string) (result *TariffPlan, err error) {
ndb := ms.db.C("tariffPlans")
result = &TariffPlan{}
err = ndb.Find(bson.M{"id": key}).One(result)
return
}
func (ms *MongoStorage) SetTariffPlan(tp *TariffPlan) error {
ndb := ms.db.C("tariffPlans")
return ndb.Insert(&tp)
}
func (ms *MongoStorage) GetUserBalance(key string) (result *UserBalance, err error) {
ndb := ms.db.C("userBalance")
result = &UserBalance{}
err = ndb.Find(bson.M{"id": key}).One(result)
return
}
func (ms *MongoStorage) SetUserBalance(ub *UserBalance) error {
ndb := ms.db.C("userBalance")
return ndb.Insert(&ub)
}

View File

@@ -20,7 +20,7 @@ package timespans
import (
"github.com/simonz05/godis"
"strings"
"encoding/json"
)
type RedisStorage struct {
@@ -47,31 +47,20 @@ func (rs *RedisStorage) GetActivationPeriodsOrFallback(key string) (aps []*Activ
if err != nil {
return
}
valuesString := elem.String()
values := strings.Split(valuesString, "\n")
if len(values) > 1 {
for _, ap_string := range values {
if len(ap_string) > 0 {
ap := &ActivationPeriod{}
ap.restore(ap_string)
aps = append(aps, ap)
}
}
} else { // fallback case
fallbackKey = valuesString
err = json.Unmarshal(elem, &aps)
if err != nil {
err = json.Unmarshal(elem, &fallbackKey)
}
return
}
func (rs *RedisStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) error {
func (rs *RedisStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) (err error) {
//.db.Select(rs.dbNb)
result := ""
var result []byte
if len(aps) > 0 {
for _, ap := range aps {
result += ap.store() + "\n"
}
result, err = json.Marshal(aps)
} else {
result = fallbackKey
result, err = json.Marshal(fallbackKey)
}
return rs.db.Set(key, result)
}
@@ -80,39 +69,41 @@ func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error
//rs.db.Select(rs.dbNb + 1)
if values, err := rs.db.Get(key); err == nil {
dest = &Destination{Id: key}
dest.restore(values.String())
err = json.Unmarshal(values, dest)
}
return
}
func (rs *RedisStorage) SetDestination(dest *Destination) error {
func (rs *RedisStorage) SetDestination(dest *Destination) (err error) {
//rs.db.Select(rs.dbNb + 1)
return rs.db.Set(dest.Id, dest.store())
result, err := json.Marshal(dest)
return rs.db.Set(dest.Id, result)
}
func (rs *RedisStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
func (rs *RedisStorage) GetActions(key string) (as []*Action, 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())
err = json.Unmarshal(values, as)
}
return
}
func (rs *RedisStorage) SetTariffPlan(tp *TariffPlan) error {
func (rs *RedisStorage) SetActions(key string, as []*Action) (err error) {
//rs.db.Select(rs.dbNb + 2)
return rs.db.Set(tp.Id, tp.store())
result, err := json.Marshal(as)
return rs.db.Set(key, result)
}
func (rs *RedisStorage) GetUserBalance(key string) (ub *UserBalance, err error) {
//rs.db.Select(rs.dbNb + 3)
if values, err := rs.db.Get(key); err == nil {
ub = &UserBalance{Id: key}
ub.restore(values.String())
err = json.Unmarshal(values, ub)
}
return
}
func (rs *RedisStorage) SetUserBalance(ub *UserBalance) error {
func (rs *RedisStorage) SetUserBalance(ub *UserBalance) (err error) {
//rs.db.Select(rs.dbNb + 3)
return rs.db.Set(ub.Id, ub.store())
result, err := json.Marshal(ub)
return rs.db.Set(ub.Id, result)
}

View File

@@ -1,62 +0,0 @@
/*
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 (
// "log"
"bytes"
"encoding/gob"
)
const (
// Direction type
INBOUND = "IN"
OUTBOUND = "OUT"
// Balance types
CREDIT = "MONETARY"
SMS = "SMS"
TRAFFIC = "INTERNET"
)
/*
Structure describing a tariff plan's number of bonus items. It is uset to restore
these numbers to the user balance every month.
*/
type TariffPlan struct {
Id string
BalanceMap map[string]float64
Actions []*Action
MinuteBuckets []*MinuteBucket
}
/*
Serializes the tariff plan for the storage. Used for key-value storages.
*/
func (tp *TariffPlan) store() (result string) {
buf := new(bytes.Buffer)
gob.NewEncoder(buf).Encode(tp)
return buf.String()
}
/*
De-serializes the tariff plan for the storage. Used for key-value storages.
*/
func (tp *TariffPlan) restore(input string) {
gob.NewDecoder(bytes.NewBuffer([]byte(input))).Decode(tp)
}

View File

@@ -1,129 +0,0 @@
/*
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 (
// "log"
//"reflect"
//"testing"
)
/*func TestTariffPlanStoreRestore(t *testing.T) {
b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "retea"}
rcb := &RecivedCallBonus{}
vd := &VolumeDiscount{100, 10}
seara := &TariffPlan{Id: "seara_voo",
SmsCredit: 100,
ReceivedCallSecondsLimit: 0,
RecivedCallBonus: rcb,
MinuteBuckets: []*MinuteBucket{b1, b2},
VolumeDiscountThresholds: []*VolumeDiscount{vd}}
s := seara.store()
tp1 := &TariffPlan{Id: "seara_voo"}
tp1.restore(s)
if tp1.store() != s {
t.Errorf("Expected %q was %q", s, tp1.store())
}
}
func TestTariffPlanKyotoStore(t *testing.T) {
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "retea"}
vd := &VolumeDiscount{100, 10}
seara := &TariffPlan{Id: "seara_voo", SmsCredit: 100, ReceivedCallSecondsLimit: 0,
MinuteBuckets: []*MinuteBucket{b1, b2}, VolumeDiscountThresholds: []*VolumeDiscount{vd}}
getter.SetTariffPlan(seara)
result, _ := getter.GetTariffPlan(seara.Id)
if !reflect.DeepEqual(seara, result) {
t.Errorf("Expected %q was %q", seara, result)
}
}
func TestTariffPlanRedisStore(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "retea"}
vd := &VolumeDiscount{100, 10}
seara := &TariffPlan{Id: "seara_voo", SmsCredit: 100, ReceivedCallSecondsLimit: 0,
MinuteBuckets: []*MinuteBucket{b1, b2}, VolumeDiscountThresholds: []*VolumeDiscount{vd}}
getter.SetTariffPlan(seara)
result, _ := getter.GetTariffPlan(seara.Id)
if !reflect.DeepEqual(seara, result) {
t.Errorf("Expected %q was %q", seara, result)
}
}
func TestTariffPlanMongoStore(t *testing.T) {
getter, _ := NewMongoStorage("127.0.0.1", "test")
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "retea"}
vd := &VolumeDiscount{100, 10}
seara := &TariffPlan{Id: "seara_voo", SmsCredit: 100, ReceivedCallSecondsLimit: 0,
MinuteBuckets: []*MinuteBucket{b1, b2}, VolumeDiscountThresholds: []*VolumeDiscount{vd}}
getter.SetTariffPlan(seara)
result, _ := getter.GetTariffPlan(seara.Id)
if !reflect.DeepEqual(seara, result) {
t.Log(seara)
t.Log(result)
t.Errorf("Expected %v was %v", seara.VolumeDiscountThresholds, result.VolumeDiscountThresholds)
}
}
*/
/********************************* Benchmarks **********************************/
/*func BenchmarkTariffPlanKyotoStoreRestore(b *testing.B) {
b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara_other", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
for i := 0; i < b.N; i++ {
getter.SetTariffPlan(seara)
getter.GetTariffPlan(seara.Id)
}
}
func BenchmarkTariffPlanRedisStoreRestore(b *testing.B) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara_other", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
for i := 0; i < b.N; i++ {
getter.SetTariffPlan(seara)
getter.GetTariffPlan(seara.Id)
}
}
func BenchmarkTariffPlanMongoStoreRestore(b *testing.B) {
getter, _ := NewMongoStorage("127.0.0.1", "test")
defer getter.Close()
b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "nationale"}
b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "retea"}
seara := &TariffPlan{Id: "seara_other", SmsCredit: 100, MinuteBuckets: []*MinuteBucket{b1, b2}}
for i := 0; i < b.N; i++ {
getter.SetTariffPlan(seara)
getter.GetTariffPlan(seara.Id)
}
}
*/

View File

@@ -21,14 +21,18 @@ package timespans
import (
// "log"
"sort"
"bytes"
"encoding/gob"
"sync"
)
const (
UB_TYPE_POSTPAID = "postpaid"
UB_TYPE_PREPAID = "prepaid"
// Direction type
INBOUND = "IN"
OUTBOUND = "OUT"
// Balance types
CREDIT = "MONETARY"
SMS = "SMS"
TRAFFIC = "INTERNET"
)
var (
@@ -39,14 +43,13 @@ var (
Structure containing information about user's credit (minutes, cents, sms...).'
*/
type UserBalance struct {
Id string
Type string // prepaid-postpaid
BalanceMap map[string]float64
UnitsCounters []*UnitsCounter
TariffPlanId string
tariffPlan *TariffPlan
MinuteBuckets []*MinuteBucket
mux sync.RWMutex
Id string
Type string // prepaid-postpaid
BalanceMap map[string]float64
MinuteBuckets []*MinuteBucket
InUnitsCounters []*UnitsCounter
OutUnitsCounters []*UnitsCounter
ActionTriggers []*ActionTrigger
}
/*
@@ -77,32 +80,6 @@ func (bs bucketsorter) Less(j, i int) bool {
bs[i].Price > bs[j].Price
}
/*
Serializes the user balance for the storage. Used for key-value storage.
*/
func (ub *UserBalance) store() (result string) {
buf := new(bytes.Buffer)
gob.NewEncoder(buf).Encode(ub)
return buf.String()
}
/*
De-serializes the user balance for the storage. Used for key-value storage.
*/
func (ub *UserBalance) restore(input string) {
gob.NewDecoder(bytes.NewBuffer([]byte(input))).Decode(ub)
}
/*
Returns the tariff plan loading it from the storage if necessary.
*/
func (ub *UserBalance) getTariffPlan() (tp *TariffPlan, err error) {
if ub.tariffPlan == nil && ub.TariffPlanId != "" {
ub.tariffPlan, err = storageGetter.GetTariffPlan(ub.TariffPlanId)
}
return ub.tariffPlan, err
}
/*
Returns user's available minutes for the specified destination
*/
@@ -138,8 +115,6 @@ func (ub *UserBalance) getSecondsForPrefix(prefix string) (seconds float64, buck
Debits some amount of user's money credit. Returns the remaining credit in user's balance.
*/
func (ub *UserBalance) debitMoneyBalance(amount float64) float64 {
ub.mux.Lock()
defer ub.mux.Unlock()
ub.BalanceMap[CREDIT] -= amount
storageGetter.SetUserBalance(ub)
return ub.BalanceMap[CREDIT]
@@ -152,8 +127,6 @@ If the amount is bigger than the sum of all seconds in the minute buckets than n
debited and an error will be returned.
*/
func (ub *UserBalance) debitMinutesBalance(amount float64, prefix string) error {
ub.mux.Lock()
defer ub.mux.Unlock()
avaliableNbSeconds, bucketList := ub.getSecondsForPrefix(prefix)
if avaliableNbSeconds < amount {
return new(AmountTooBig)
@@ -199,8 +172,6 @@ Debits some amount of user's SMS balance. Returns the remaining SMS in user's ba
If the amount is bigger than the balance than nothing wil be debited and an error will be returned
*/
func (ub *UserBalance) debitSMSBuget(amount float64) (float64, error) {
ub.mux.Lock()
defer ub.mux.Unlock()
if ub.BalanceMap[SMS] < amount {
return ub.BalanceMap[SMS], new(AmountTooBig)
}
@@ -214,8 +185,6 @@ func (ub *UserBalance) debitSMSBuget(amount float64) (float64, error) {
Adds the specified amount of seconds.
*/
// func (ub *UserBalance) addReceivedCallSeconds(direction, tor, destination string, amount float64) error {
// ub.mux.Lock()
// defer ub.mux.Unlock()
// ub.ReceivedCallSeconds += amount
// if tariffPlan, err := ub.getTariffPlan(); tariffPlan != nil && err == nil {
// if ub.ReceivedCallSeconds >= tariffPlan.ReceivedCallSecondsLimit {
@@ -240,10 +209,8 @@ Adds the specified amount of seconds.
/*
Resets the user balance items to their tariff plan values.
*/
func (ub *UserBalance) resetUserBalance() (err error) {
ub.mux.Lock()
defer ub.mux.Unlock()
if tp, err := ub.getTariffPlan(); err == nil {
/*func (ub *UserBalance) resetUserBalance() (err error) {
if tp, err := ub.getAccountActions(); err == nil {
for k, _ := range ub.BalanceMap {
ub.BalanceMap[k] = tp.BalanceMap[k]
}
@@ -259,3 +226,4 @@ func (ub *UserBalance) resetUserBalance() (err error) {
}
return
}
*/