Browse Source

Prepare priced CDRs for export

master
Gerdriaan Mulder 6 years ago
parent
commit
63ad1bb64c
  1. 38
      cmd/cdrtool/main.go
  2. 34
      combine.go
  3. 2
      import_price.go
  4. 13
      record.go
  5. 76
      sortable_records.go

38
cmd/cdrtool/main.go

@ -5,6 +5,7 @@ import (
"fmt"
"log"
"os"
"sort"
"src.wolkict.net/cdr"
)
@ -15,6 +16,7 @@ var (
priceExec = flag.Bool("execute", false, "price each CDR according to the prices file")
debugImport = flag.Bool("debugImport", false, "show extra debugging info for import")
debug = flag.Bool("debug", false, "show extra debugging info")
export = flag.String("export", "", "export priced calls to this csv")
)
func usage() {
@ -24,6 +26,7 @@ func usage() {
func main() {
flag.Parse()
cdr.SetDebug(*debug)
if *csvFile == "" {
usage()
@ -95,5 +98,40 @@ func main() {
fmt.Printf("c: %+v\n", c)
}
}
if *export != "" {
sc := cdr.NewSortableCalls(calls)
sort.Sort(sc)
callCost := 0
callPrice := 0
for _, c := range sc.Calls {
fmt.Printf("%s\n", c.String())
callCost += c.Cost
callPrice += c.Price
}
st := cdr.NewSortableTexts(texts)
sort.Sort(st)
textCost := 0
textPrice := 0
for _, t := range st.Texts {
fmt.Printf("%s\n", t.String())
textCost += t.PricedLine.Cost
textPrice += t.PricedLine.Price
}
sd := cdr.NewSortableData(data)
sort.Sort(sd)
dataCost := 0
dataPrice := 0
for _, d := range sd.Data {
fmt.Printf("%s\n", d.String())
dataCost += d.PricedLine.Cost
dataPrice += d.PricedLine.Price
}
fmt.Printf("callCost: %.4f, callPrice: %.4f\n", float64(callCost)/10000.0, float64(callPrice)/10000.0)
fmt.Printf("textCost: %.4f, textPrice: %.4f\n", float64(textCost)/10000.0, float64(textPrice)/10000.0)
fmt.Printf("dataCost: %.4f, dataPrice: %.4f\n", float64(dataCost)/10000.0, float64(dataPrice)/10000.0)
}
}
}

34
combine.go

@ -7,6 +7,14 @@ import (
"time"
)
var (
DEBUG = false
)
func SetDebug(d bool) {
DEBUG = d
}
func CombineTextCDRs(cdrLines []Line, prices []Price) ([]Text, error) {
var err []string
@ -113,9 +121,19 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
destinationToPrice[CALL_NATIONAL_DESTINATION_AIRTIME_TO_HANDSET.Destination] = CALL_NATIONAL_DESTINATION_AIRTIME_TO_HANDSET
destinationToPrice[CALL_NATIONAL_DESTINATION_AIRTIME_FROM_HANDSET.Destination] = CALL_NATIONAL_DESTINATION_AIRTIME_FROM_HANDSET
destinationToPrice[CALL_NATIONAL_DESTINATION_MOBILE.Destination] = CALL_NATIONAL_DESTINATION_MOBILE
destinationToPrice[CALL_NATIONAL_DESTINATION_MOBILE_SU.Destination] = CALL_NATIONAL_DESTINATION_MOBILE_SU
destinationToPrice[CALL_NATIONAL_DESTINATION_FREEPHONE.Destination] = CALL_NATIONAL_DESTINATION_FREEPHONE
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_NOSETUP.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_NOSETUP
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_BIBA.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_BIBA
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_FREE_NOSETUP.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_FREE_NOSETUP
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_10ct.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_10ct
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_45ct.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_45ct
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_50ct_NOSETUP.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_50ct_NOSETUP
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_80ct_NOSETUP.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_80ct_NOSETUP
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_50ct_CALL.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_50ct_CALL
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_80ct_CALL.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_80ct_CALL
destinationToPrice[CALL_NATIONAL_DESTINATION_VOICEMAIL.Destination] = CALL_NATIONAL_DESTINATION_VOICEMAIL
destinationToPrice[CALL_INTERNAL.Destination] = CALL_INTERNAL
destinationToPrice[CALL_UK_DESTINATION_NON_GEO.Destination] = CALL_UK_DESTINATION_NON_GEO
nonMatchedDestinations := make(map[string]struct{})
for _, l := range cdrLines {
@ -129,7 +147,7 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
priceLoop:
for _, p := range prices {
if p.Type != PriceCall {
fmt.Printf("[debug] skipping this price: %+v\n", p)
fmt.Printf("[warning] skipping this price: %+v\n", p)
continue priceLoop
}
if p.Destination == l.Destination {
@ -140,7 +158,7 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
}
if !matched {
fmt.Printf("[debug] non-matched price for destination %q (from line: %+v)\n", l.Destination, l)
fmt.Printf("[warning] non-matched price for destination %q (from line: %+v)\n", l.Destination, l)
nonMatchedDestinations[l.Destination] = struct{}{}
}
}
@ -233,7 +251,9 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
callCost += thisLegCost
callPrice += thisLegPrice
fmt.Printf("(%12s( %12s)->%12s) leg %d, callCost: %.4f, callPrice: %.4f (dst: %q)\n", leg.CLI, leg.From, leg.To, leg.Leg, callCost, callPrice, legPrice.OurRef)
if DEBUG {
fmt.Printf("(%12s( %12s)->%12s) leg %d, callCost: %.4f, callPrice: %.4f (dst: %q)\n", leg.CLI, leg.From, leg.To, leg.Leg, callCost/10000.0, callPrice/10000.0, legPrice.OurRef)
}
}
// Source -> HS (non-roaming) (i.e. incoming calls) does not exist in the CSV
//fmt.Printf("(%12s->%12s) leg %d, price: %+v\n", leg.CLI, leg.To, leg.Leg, legPrice)
@ -268,7 +288,7 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
ret[i].From = callFrom
ret[i].To = callTo
fmt.Printf(" ==> %+v\n\n", ret[i])
//fmt.Printf(" ==> %+v\n\n", ret[i])
//ret[i].Legs = make([]PricedLine, len(v))
//for j, leg := range v {
//ret[i].Legs[j] = leg
@ -276,6 +296,6 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
i++
}
fmt.Printf("destinationPrice: %+v\n", destinationToPrice)
return nil, fmt.Errorf("not yet implemented")
//fmt.Printf("destinationPrice: %+v\n", destinationToPrice)
return ret, nil
}

2
import_price.go

@ -97,7 +97,7 @@ func ImportPricesFile(fn string) ([]Price, error) {
for _, priceType := range priceTypes {
var destination string
if line[PriceImportRegion] == "Europe" {
if line[PriceImportRegion] == "Europe" || line[PriceImportRegion] == "North America" || line[PriceImportRegion] == "Africa" {
destination = line[PriceImportDestination] + " - " + priceType + " - "
if priceType == "Fixed" {
destination = destination + "Proper"

13
record.go

@ -1,6 +1,7 @@
package cdr
import (
"fmt"
"time"
)
@ -136,14 +137,26 @@ type Call struct {
Legs []PricedLine
}
func (c *Call) String() string {
return fmt.Sprintf("%s (%12s->%12s) callCost: %.4f, callPrice: %.4f (dur: %v)", c.Time.Format(DateTimeFormat), c.From, c.To, float64(c.Cost)/10000.0, float64(c.Price)/10000.0, c.Duration)
}
// Text is a specific PricedLine for a text message.
type Text struct {
PricedLine
}
func (t *Text) String() string {
return fmt.Sprintf("%s (%12s->%12s) textCost: %.4f, textPrice: %.4f", t.PricedLine.Line.Time.Format(DateTimeFormat), t.PricedLine.Line.From, t.PricedLine.Line.To, float64(t.PricedLine.Cost)/10000.0, float64(t.PricedLine.Price)/10000.0)
}
// Data is a specific PricedLine that annotates the number of bytes consumed in that session.
type Data struct {
PricedLine
Bytes int
}
func (d *Data) String() string {
return fmt.Sprintf("%s (%v bytes) dataCost: %.4f, dataPrice: %.4f", d.PricedLine.Line.Time.Format(DateTimeFormat), d.Bytes, float64(d.PricedLine.Cost)/10000.0, float64(d.PricedLine.Price)/10000.0)
}

76
sortable_records.go

@ -0,0 +1,76 @@
package cdr
type SortableCalls struct {
Calls []Call
num int // shortcut for Len()
}
func NewSortableCalls(c []Call) *SortableCalls {
return &SortableCalls{
Calls: append(c[:0:0], c...),
num: len(c),
}
}
func (sc *SortableCalls) Len() int {
return sc.num
}
func (sc *SortableCalls) Less(i, j int) bool {
// We assume i, j < len(sc.Calls)
return sc.Calls[i].Time.Before(sc.Calls[j].Time)
}
func (sc *SortableCalls) Swap(i, j int) {
sc.Calls[j], sc.Calls[i] = sc.Calls[i], sc.Calls[j]
}
type SortableTexts struct {
Texts []Text
num int // shortcut for Len()
}
func NewSortableTexts(t []Text) *SortableTexts {
return &SortableTexts{
Texts: append(t[:0:0], t...),
num: len(t),
}
}
func (st *SortableTexts) Len() int {
return st.num
}
func (st *SortableTexts) Less(i, j int) bool {
// We assume i, j < len(st.Texts)
return st.Texts[i].PricedLine.Line.Time.Before(st.Texts[j].PricedLine.Line.Time)
}
func (st *SortableTexts) Swap(i, j int) {
st.Texts[j], st.Texts[i] = st.Texts[i], st.Texts[j]
}
type SortableData struct {
Data []Data
num int // shortcut for Len()
}
func NewSortableData(d []Data) *SortableData {
return &SortableData{
Data: append(d[:0:0], d...),
num: len(d),
}
}
func (sd *SortableData) Len() int {
return sd.num
}
func (sd *SortableData) Less(i, j int) bool {
// We assume i, j < len(st.Data)
return sd.Data[i].PricedLine.Line.Time.Before(sd.Data[j].PricedLine.Line.Time)
}
func (sd *SortableData) Swap(i, j int) {
sd.Data[j], sd.Data[i] = sd.Data[i], sd.Data[j]
}
Loading…
Cancel
Save