added priced units

This commit is contained in:
Radu Ioan Fericean
2012-07-23 17:13:08 +03:00
parent 0dd9898447
commit 79d67bcf39
10 changed files with 79 additions and 163 deletions

View File

@@ -1,102 +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 main
import (
"bufio"
"database/sql"
"encoding/csv"
"flag"
"fmt"
_ "github.com/bmizerany/pq"
"github.com/cgrates/cgrates/timespans"
"log"
"net/rpc/jsonrpc"
"os"
"time"
)
var (
cdrFile = flag.String("freeswitchcdr", "Master.csv", "Freeswitch Master CSV CDR file.")
resultFile = flag.String("resultfile", "out.csv", "Generated file containing CDR and price info.")
host = flag.String("host", "localhost", "The host to connect to. Values that start with / are for UNIX domain sockets.")
port = flag.String("port", "5432", "The port to bind to.")
dbName = flag.String("dbname", "cgrates", "The name of the database to connect to.")
user = flag.String("user", "", "The user to sign in as.")
password = flag.String("password", "", "The user's password.")
)
func readDbRecord(db *sql.DB, searchedUUID string) (cc *timespans.CallCost, timespansText string, err error) {
row := db.QueryRow(fmt.Sprintf("SELECT * FROM callcosts WHERE uuid='%s'", searchedUUID))
var uuid string
cc = &timespans.CallCost{}
err = row.Scan(&uuid, &cc.Direction, &cc.Tenant, &cc.TOR, &cc.Subject, &cc.Destination, &cc.Cost, &cc.ConnectFee, &timespansText)
return
}
func main() {
flag.Parse()
useDB := true
file, err := os.Open(*cdrFile)
defer file.Close()
if err != nil {
log.Fatal(err)
}
db, err := sql.Open("postgres", fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=disable", *host, *port, *dbName, *user, *password))
defer db.Close()
if err != nil {
log.Printf("failed to open the database: %v", err)
useDB = false
}
csvReader := csv.NewReader(bufio.NewReader(file))
client, err := jsonrpc.Dial("tcp", "localhost:2001")
useRPC := true
if err != nil {
log.Printf("Could not connect to rater server: %v!", err)
useRPC = false
}
for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() {
uuid := record[10]
t, _ := time.Parse("2012-05-21 17:48:20", record[5])
fmt.Println(t)
if useDB {
cc, timespansText, err := readDbRecord(db, uuid)
if err != nil && useRPC {
// try getting the price from the rater
tenant := record[0]
subject := record[1]
dest := record[2]
t1, _ := time.Parse("2012-05-21 17:48:20", record[5])
t2, _ := time.Parse("2012-05-21 17:48:20", record[6])
cd := timespans.CallDescriptor{
Direction: "OUT",
Tenant: tenant,
TOR: "0",
Subject: subject,
Destination: dest,
TimeStart: t1,
TimeEnd: t2}
client.Call("Responder.GetCost", cd, cc)
}
_ = timespansText
//log.Print(cc, timespansText)
}
}
}

View File

@@ -27,11 +27,11 @@ import (
)
type Rate struct {
DestinationsTag string
ConnectFee, Price, BillingUnit float64
DestinationsTag string
ConnectFee, Price, PricedUnits, RateIncrements float64
}
func NewRate(destinationsTag, connectFee, price, billingUnit string) (r *Rate, err error) {
func NewRate(destinationsTag, connectFee, price, pricedUnits, rateIncrements string) (r *Rate, err error) {
cf, err := strconv.ParseFloat(connectFee, 64)
if err != nil {
log.Printf("Error parsing connect fee from: %v", connectFee)
@@ -42,16 +42,22 @@ func NewRate(destinationsTag, connectFee, price, billingUnit string) (r *Rate, e
log.Printf("Error parsing price from: %v", price)
return
}
bu, err := strconv.ParseFloat(billingUnit, 64)
pu, err := strconv.ParseFloat(pricedUnits, 64)
if err != nil {
log.Printf("Error parsing billing unit from: %v", billingUnit)
log.Printf("Error parsing priced units from: %v", pricedUnits)
return
}
ri, err := strconv.ParseFloat(rateIncrements, 64)
if err != nil {
log.Printf("Error parsing rates increments from: %v", rateIncrements)
return
}
r = &Rate{
DestinationsTag: destinationsTag,
ConnectFee: cf,
Price: p,
BillingUnit: bu,
PricedUnits: pu,
RateIncrements: ri,
}
return
}
@@ -99,14 +105,15 @@ func NewRateTiming(ratesTag string, timing *Timing, weight string) (rt *RateTimi
func (rt *RateTiming) GetInterval(r *Rate) (i *timespans.Interval) {
i = &timespans.Interval{
Months: rt.timing.Months,
MonthDays: rt.timing.MonthDays,
WeekDays: rt.timing.WeekDays,
StartTime: rt.timing.StartTime,
Weight: rt.Weight,
ConnectFee: r.ConnectFee,
Price: r.Price,
BillingUnit: r.BillingUnit,
Months: rt.timing.Months,
MonthDays: rt.timing.MonthDays,
WeekDays: rt.timing.WeekDays,
StartTime: rt.timing.StartTime,
Weight: rt.Weight,
ConnectFee: r.ConnectFee,
Price: r.Price,
PricedUnits: r.PricedUnits,
RateIncrements: r.RateIncrements,
}
return
}

View File

@@ -19,9 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package main
import (
"fmt"
"github.com/cgrates/cgrates/timespans"
"log"
"fmt"
"time"
)
@@ -78,7 +78,7 @@ func (csvr *CSVReader) loadRates(fn string) {
// skip header line
continue
}
r, err := NewRate(record[1], record[2], record[3], record[4])
r, err := NewRate(record[1], record[2], record[3], record[4], record[5])
if err != nil {
continue
}

View File

@@ -1,9 +1,9 @@
Tag,DestinationsTag,ConnectFee,Price,BillingUnit
RT_STANDARD,GERMANY,0,0.2,1
RT_STANDARD,GERMANY_O2,0,0.1,1
RT_STANDARD,GERMANY_PREMIUM,0,0.1,1
RT_DEFAULT,ALL,0,0.1,1
RT_STD_WEEKEND,GERMANY,0,0.1,1
RT_STD_WEEKEND,GERMANY_O2,0,0.05,1
P1,NAT,0,1,1
P2,NAT,0,0.5,1
Tag,DestinationsTag,ConnectFee,Price,PricedUnits,RateIncrements
RT_STANDARD,GERMANY,0,0.2,60,1
RT_STANDARD,GERMANY_O2,0,0.1,60,1
RT_STANDARD,GERMANY_PREMIUM,0,0.1,60,1
RT_DEFAULT,ALL,0,0.1,60,1
RT_STD_WEEKEND,GERMANY,0,0.1,60,1
RT_STD_WEEKEND,GERMANY_O2,0,0.05,60,1
P1,NAT,0,1,1,1
P2,NAT,0,0.5,1,1
1 Tag DestinationsTag ConnectFee Price PricedUnits BillingUnit RateIncrements
2 RT_STANDARD GERMANY 0 0.2 60 1
3 RT_STANDARD GERMANY_O2 0 0.1 60 1
4 RT_STANDARD GERMANY_PREMIUM 0 0.1 60 1
5 RT_DEFAULT ALL 0 0.1 60 1
6 RT_STD_WEEKEND GERMANY 0 0.1 60 1
7 RT_STD_WEEKEND GERMANY_O2 0 0.05 60 1
8 P1 NAT 0 1 1 1
9 P2 NAT 0 0.5 1 1

View File

@@ -31,15 +31,16 @@ func init() {
func TestActionTimingStoreRestore(t *testing.T) {
i := &Interval{
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{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},
WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Price: 1.0,
BillingUnit: 1.0,
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{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},
WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Price: 1.0,
PricedUnits: 60,
RateIncrements: 1,
}
at := &ActionTiming{
Id: "some uuid",
@@ -50,7 +51,7 @@ func TestActionTimingStoreRestore(t *testing.T) {
ActionsId: "Commando",
}
r := at.store()
if string(r) != "some uuid|test|one,two,three|1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5;18:00:00;00:00:00;10;0;1;1|10|Commando" {
if string(r) != "some uuid|test|one,two,three|1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1|10|Commando" {
t.Errorf("Error serializing action timing: %v", string(r))
}
o := &ActionTiming{}

View File

@@ -19,9 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package timespans
import (
"encoding/json"
"reflect"
"testing"
"encoding/json"
"time"
//"log"
)
@@ -42,7 +42,7 @@ func TestApStoreRestore(t *testing.T) {
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"
expected := "1328106601000000000|2;1;3,4;14:30:00;15:00:00;0;0;0;0;0"
if result != expected {
t.Errorf("Expected %q was %q", expected, result)
}
@@ -76,7 +76,7 @@ func TestApStoreRestoreJson(t *testing.T) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
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}]}"
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,\"PricedUnits\":0,\"RateIncrements\":0}]}"
if string(result) != expected {
t.Errorf("Expected %q was %q", expected, result)
}
@@ -93,7 +93,7 @@ func TestApStoreRestoreBlank(t *testing.T) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
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}]}"
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0}]}"
if string(result) != expected {
t.Errorf("Expected %q was %q", expected, result)
}

View File

@@ -31,11 +31,11 @@ import (
Defines a time interval for which a certain set of prices will apply
*/
type Interval struct {
Months Months
MonthDays MonthDays
WeekDays WeekDays
StartTime, EndTime string // ##:##:## format
Weight, ConnectFee, Price, BillingUnit float64
Months Months
MonthDays MonthDays
WeekDays WeekDays
StartTime, EndTime string // ##:##:## format
Weight, ConnectFee, Price, PricedUnits, RateIncrements float64
}
/*
@@ -139,7 +139,8 @@ func (i *Interval) store() (result string) {
result += strconv.FormatFloat(i.Weight, 'f', -1, 64) + ";"
result += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + ";"
result += strconv.FormatFloat(i.Price, 'f', -1, 64) + ";"
result += strconv.FormatFloat(i.BillingUnit, 'f', -1, 64)
result += strconv.FormatFloat(i.PricedUnits, 'f', -1, 64) + ";"
result += strconv.FormatFloat(i.RateIncrements, 'f', -1, 64)
return
}
@@ -156,5 +157,6 @@ func (i *Interval) restore(input string) {
i.Weight, _ = strconv.ParseFloat(is[5], 64)
i.ConnectFee, _ = strconv.ParseFloat(is[6], 64)
i.Price, _ = strconv.ParseFloat(is[7], 64)
i.BillingUnit, _ = strconv.ParseFloat(is[8], 64)
i.PricedUnits, _ = strconv.ParseFloat(is[8], 64)
i.RateIncrements, _ = strconv.ParseFloat(is[9], 64)
}

View File

@@ -26,18 +26,19 @@ import (
func TestIntervalStoreRestore(t *testing.T) {
i := &Interval{
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{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},
WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Price: 1.0,
BillingUnit: 1.0,
Months: Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December},
MonthDays: MonthDays{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},
WeekDays: WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday},
StartTime: "18:00:00",
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Price: 1.0,
PricedUnits: 60,
RateIncrements: 1,
}
r := i.store()
if string(r) != "1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5;18:00:00;00:00:00;10;0;1;1" {
if string(r) != "1,2,3,4,5,6,7,8,9,10,11,12;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;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1" {
t.Errorf("Error serializing interval: %v", string(r))
}
o := &Interval{}

View File

@@ -21,6 +21,7 @@ package timespans
import (
"fmt"
//"log"
"math"
"time"
)
@@ -59,10 +60,15 @@ func (ts *TimeSpan) getCost(cd *CallDescriptor) (cost float64) {
if ts.Interval == nil {
return 0
}
if ts.Interval.BillingUnit > 0 {
cost = (ts.GetDuration().Seconds() / ts.Interval.BillingUnit) * ts.Interval.Price
duration := ts.GetDuration().Seconds()
i := ts.Interval
if i.RateIncrements == 0 {
i.RateIncrements = 1
}
if i.PricedUnits != 0 {
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * (i.Price / i.PricedUnits)
} else {
cost = ts.GetDuration().Seconds() * ts.Interval.Price
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * i.Price
}
// if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
// userBalance.mux.RLock()

View File

@@ -196,8 +196,9 @@ func TestTimespanGetCost(t *testing.T) {
if ts1.getCost(cd) != 600 {
t.Error("Expected 10 got ", ts1.getCost(cd))
}
ts1.Interval.BillingUnit = .1
if ts1.getCost(cd) != 6000 {
ts1.Interval.PricedUnits = 60
ts1.Interval.RateIncrements = 1
if ts1.getCost(cd) != 10 {
t.Error("Expected 6000 got ", ts1.getCost(cd))
}
}