From a7b1d906046117c627703dbe3d768f8b4a361a8d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 13 Jun 2012 18:31:28 +0300 Subject: [PATCH] switched to json encoding, removed mongo and kyoto and started actions importing --- cmd/cgr-loader/{budgets.go => actions.go} | 60 +++++++-- cmd/cgr-loader/cgr-loader.go | 40 +++--- cmd/cgr-loader/helpers.go | 10 +- data/AccountActions.csv | 3 + data/ActionTimings.csv | 6 + data/ActionTriggers.csv | 5 + data/Actions.csv | 8 ++ data/DestinationBalanceActions.csv | 4 - data/PrimaryBalanceActions.csv | 6 - data/Timings.csv | 18 +-- data/test.kch | Bin 6299424 -> 0 bytes timespans/actions.go | 40 +++--- timespans/activationperiod.go | 48 -------- timespans/activationperiod_test.go | 45 ++----- timespans/calldesc.go | 8 +- timespans/calldesc_test.go | 142 ---------------------- timespans/dateseries.go | 100 ++++++--------- timespans/dateseries_test.go | 19 +-- timespans/destinations.go | 19 --- timespans/destinations_test.go | 77 +++--------- timespans/minute_buckets.go | 24 ---- timespans/storage_interface.go | 4 +- timespans/storage_kyoto.go | 110 ----------------- timespans/storage_mongo.go | 109 ----------------- timespans/storage_redis.go | 51 ++++---- timespans/tariff_plans.go | 62 ---------- timespans/tariff_plans_test.go | 129 -------------------- timespans/userbalance.go | 66 +++------- 28 files changed, 243 insertions(+), 970 deletions(-) rename cmd/cgr-loader/{budgets.go => actions.go} (52%) create mode 100644 data/AccountActions.csv create mode 100644 data/ActionTimings.csv create mode 100644 data/ActionTriggers.csv create mode 100644 data/Actions.csv delete mode 100644 data/DestinationBalanceActions.csv delete mode 100644 data/PrimaryBalanceActions.csv delete mode 100644 data/test.kch delete mode 100644 timespans/storage_kyoto.go delete mode 100644 timespans/storage_mongo.go delete mode 100644 timespans/tariff_plans.go delete mode 100644 timespans/tariff_plans_test.go diff --git a/cmd/cgr-loader/budgets.go b/cmd/cgr-loader/actions.go similarity index 52% rename from cmd/cgr-loader/budgets.go rename to cmd/cgr-loader/actions.go index f300ea028..5dbca1d16 100644 --- a/cmd/cgr-loader/budgets.go +++ b/cmd/cgr-loader/actions.go @@ -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) } } diff --git a/cmd/cgr-loader/cgr-loader.go b/cmd/cgr-loader/cgr-loader.go index be3c49f5e..3a59a4758 100644 --- a/cmd/cgr-loader/cgr-loader.go +++ b/cmd/cgr-loader/cgr-loader.go @@ -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() } diff --git a/cmd/cgr-loader/helpers.go b/cmd/cgr-loader/helpers.go index 907b80d32..c628c2735 100644 --- a/cmd/cgr-loader/helpers.go +++ b/cmd/cgr-loader/helpers.go @@ -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 } diff --git a/data/AccountActions.csv b/data/AccountActions.csv new file mode 100644 index 000000000..25b783910 --- /dev/null +++ b/data/AccountActions.csv @@ -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 \ No newline at end of file diff --git a/data/ActionTimings.csv b/data/ActionTimings.csv new file mode 100644 index 000000000..6025e214c --- /dev/null +++ b/data/ActionTimings.csv @@ -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 \ No newline at end of file diff --git a/data/ActionTriggers.csv b/data/ActionTriggers.csv new file mode 100644 index 000000000..c9cd4da4c --- /dev/null +++ b/data/ActionTriggers.csv @@ -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 \ No newline at end of file diff --git a/data/Actions.csv b/data/Actions.csv new file mode 100644 index 000000000..25cf002b4 --- /dev/null +++ b/data/Actions.csv @@ -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 \ No newline at end of file diff --git a/data/DestinationBalanceActions.csv b/data/DestinationBalanceActions.csv deleted file mode 100644 index 45751eca5..000000000 --- a/data/DestinationBalanceActions.csv +++ /dev/null @@ -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 \ No newline at end of file diff --git a/data/PrimaryBalanceActions.csv b/data/PrimaryBalanceActions.csv deleted file mode 100644 index 99e51cdb7..000000000 --- a/data/PrimaryBalanceActions.csv +++ /dev/null @@ -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 \ No newline at end of file diff --git a/data/Timings.csv b/data/Timings.csv index 6fe72181e..25445417c 100644 --- a/data/Timings.csv +++ b/data/Timings.csv @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/data/test.kch b/data/test.kch deleted file mode 100644 index 5b6c3786c5f1aff6ee39a50ed28423265d8caee9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6299424 zcmeFtElvYr5C-5)-Vj1F33W&~0!JW0kxC2_Bp1MJynJ3foc-PI&oBOc_ey{O0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D)};DzDodca{JF z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5*3E>QXD zy{Co*2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB=Ej0+p-P1^yBsK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U7=%FOV-Ry1k^lh$1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t8wLRBl_w$s$02009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfWXKFDla1!us#6- z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7+6z>E+lNXdK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7dX?*f&(-g|0DfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB=D(0+q*>ak2;yAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D%Sqm9GXV zA_x#5K!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0tALAQ28F>*k&d`fB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pkBNwPVjaTSf+gO-nHJdK`Kz$3I#m|s) zhKmOr+c=68srneijOYCRFW(s;K!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PJ_C zfo9|U$2Ook0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+z#Rh3#=m#0S|UJz009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBoLUINX=uiq<`JOl_3AV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PE*?&}{s*Wu!0y1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oR_XG#mfb|8z}&009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72>hP}W>4$WG|xUC zpY$f9lU|w^ovf7?t!^i+z4~dHmRXV~t)!h4WtkoJ(loro-EbV_WwsHq3RpyBi-=B= zmSyeUv#WU@v&LFK|GcX;)V;r<+sfVF@{NW*Lm7Sbv-!#6sgzi?XTi~uLs?kFVE_svnTbsFXsJ+ zTD`Q}E*>zzte)o4^ID#Ct1RpDzj(UrG4!!$cyvv<^ogSoFx#~9wi)9{v z2C9}~YzRtx+v8DC8xE#lsc}n@ZK{Ol`=|Q;>>Vxl=3+9u7>thldHA7P z99i10rrc(%tM%vW$(s+8@yFq0nRS^m5 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 +} diff --git a/timespans/activationperiod.go b/timespans/activationperiod.go index df66ca933..cb33a10a9 100644 --- a/timespans/activationperiod.go +++ b/timespans/activationperiod.go @@ -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) - } -} diff --git a/timespans/activationperiod_test.go b/timespans/activationperiod_test.go index e2c58d7b3..3904ffaf1 100644 --- a/timespans/activationperiod_test.go +++ b/timespans/activationperiod_test.go @@ -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) } } diff --git a/timespans/calldesc.go b/timespans/calldesc.go index 75dc83068..5ff7529d4 100644 --- a/timespans/calldesc.go +++ b/timespans/calldesc.go @@ -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 -} +}*/ diff --git a/timespans/calldesc_test.go b/timespans/calldesc_test.go index a76ddec55..26ba213d1 100644 --- a/timespans/calldesc_test.go +++ b/timespans/calldesc_test.go @@ -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() - } -} diff --git a/timespans/dateseries.go b/timespans/dateseries.go index 20d387c29..1ebe0703d 100644 --- a/timespans/dateseries.go +++ b/timespans/dateseries.go @@ -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 + } } } } diff --git a/timespans/dateseries_test.go b/timespans/dateseries_test.go index db0403f04..5b9e09afb 100644 --- a/timespans/dateseries_test.go +++ b/timespans/dateseries_test.go @@ -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) } diff --git a/timespans/destinations.go b/timespans/destinations.go index bc2073a10..9ecc63c69 100644 --- a/timespans/destinations.go +++ b/timespans/destinations.go @@ -18,10 +18,6 @@ along with this program. If not, see 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. */ diff --git a/timespans/destinations_test.go b/timespans/destinations_test.go index 72685b039..78ee924ce 100644 --- a/timespans/destinations_test.go +++ b/timespans/destinations_test.go @@ -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) } } diff --git a/timespans/minute_buckets.go b/timespans/minute_buckets.go index eec7bbe76..e8033231f 100644 --- a/timespans/minute_buckets.go +++ b/timespans/minute_buckets.go @@ -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. */ diff --git a/timespans/storage_interface.go b/timespans/storage_interface.go index 41889ba83..e8c098f1e 100644 --- a/timespans/storage_interface.go +++ b/timespans/storage_interface.go @@ -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 } diff --git a/timespans/storage_kyoto.go b/timespans/storage_kyoto.go deleted file mode 100644 index ab011dda4..000000000 --- a/timespans/storage_kyoto.go +++ /dev/null @@ -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 -*/ - -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())) -} diff --git a/timespans/storage_mongo.go b/timespans/storage_mongo.go deleted file mode 100644 index 4e314c33d..000000000 --- a/timespans/storage_mongo.go +++ /dev/null @@ -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 -*/ - -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) -} diff --git a/timespans/storage_redis.go b/timespans/storage_redis.go index 215a5c142..14161d2ca 100644 --- a/timespans/storage_redis.go +++ b/timespans/storage_redis.go @@ -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) } diff --git a/timespans/tariff_plans.go b/timespans/tariff_plans.go deleted file mode 100644 index 0faadcdfc..000000000 --- a/timespans/tariff_plans.go +++ /dev/null @@ -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 -*/ - -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) -} diff --git a/timespans/tariff_plans_test.go b/timespans/tariff_plans_test.go deleted file mode 100644 index 3cbda5540..000000000 --- a/timespans/tariff_plans_test.go +++ /dev/null @@ -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 -*/ - -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) - } -} -*/ diff --git a/timespans/userbalance.go b/timespans/userbalance.go index fcc1b2f39..36a33497d 100644 --- a/timespans/userbalance.go +++ b/timespans/userbalance.go @@ -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 } +*/