Browse Source

Add useful information to Call/Text/Data struct for exporting, implement exporting

master
Gerdriaan Mulder 6 years ago
parent
commit
8f49e0e514
  1. 28
      cmd/cdrtool/main.go
  2. 31
      combine.go
  3. 71
      export_csv.go
  4. 16
      record.go

28
cmd/cdrtool/main.go

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/csv"
"flag" "flag"
"fmt" "fmt"
"log" "log"
@ -99,14 +100,20 @@ func main() {
} }
} }
if *export != "" { if *export != "" {
toExport := make([][]string, 0)
toExport = append(toExport, cdr.ExportLineHeader)
sc := cdr.NewSortableCalls(calls) sc := cdr.NewSortableCalls(calls)
sort.Sort(sc) sort.Sort(sc)
callCost := 0 callCost := 0
callPrice := 0 callPrice := 0
for _, c := range sc.Calls { for _, c := range sc.Calls {
if *debug {
fmt.Printf("%s\n", c.String()) fmt.Printf("%s\n", c.String())
}
callCost += c.Cost callCost += c.Cost
callPrice += c.Price callPrice += c.Price
toExport = append(toExport, cdr.ExportCall(c))
} }
st := cdr.NewSortableTexts(texts) st := cdr.NewSortableTexts(texts)
@ -114,9 +121,12 @@ func main() {
textCost := 0 textCost := 0
textPrice := 0 textPrice := 0
for _, t := range st.Texts { for _, t := range st.Texts {
if *debug {
fmt.Printf("%s\n", t.String()) fmt.Printf("%s\n", t.String())
}
textCost += t.PricedLine.Cost textCost += t.PricedLine.Cost
textPrice += t.PricedLine.Price textPrice += t.PricedLine.Price
toExport = append(toExport, cdr.ExportText(t))
} }
sd := cdr.NewSortableData(data) sd := cdr.NewSortableData(data)
@ -124,14 +134,32 @@ func main() {
dataCost := 0 dataCost := 0
dataPrice := 0 dataPrice := 0
for _, d := range sd.Data { for _, d := range sd.Data {
if *debug {
fmt.Printf("%s\n", d.String()) fmt.Printf("%s\n", d.String())
}
dataCost += d.PricedLine.Cost dataCost += d.PricedLine.Cost
dataPrice += d.PricedLine.Price dataPrice += d.PricedLine.Price
toExport = append(toExport, cdr.ExportData(d))
} }
fmt.Printf("callCost: %.4f, callPrice: %.4f\n", float64(callCost)/10000.0, float64(callPrice)/10000.0) 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("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) fmt.Printf("dataCost: %.4f, dataPrice: %.4f\n", float64(dataCost)/10000.0, float64(dataPrice)/10000.0)
_, err = os.Stat(*export)
if !os.IsNotExist(err) {
log.Fatalf("refusing to write to existing file %q for exporting", *export)
}
csvExportFile, err := os.Create(*export)
if err != nil {
log.Fatalf("cannot create export file %q: %v", *export, err)
}
csvWriter := csv.NewWriter(csvExportFile)
err = csvWriter.WriteAll(toExport)
if err != nil {
log.Fatalf("cannot export csv to %q: %v", *export, err)
}
log.Printf("exported priced CDRs to %q", *export)
} }
} }
} }

31
combine.go

@ -38,11 +38,12 @@ func CombineTextCDRs(cdrLines []Line, prices []Price) ([]Text, error) {
continue continue
} }
ret[i] = Text{ ret[i] = Text{
PricedLine{ PricedLine: PricedLine{
Line: l, Line: l,
Cost: TEXT_OUTGOING_BUY, Cost: TEXT_OUTGOING_BUY,
Price: TEXT_OUTGOING_SELL, Price: TEXT_OUTGOING_SELL,
}, },
Account: l.Account,
} }
i++ i++
} }
@ -103,7 +104,8 @@ func CombineDataCDRs(cdrLines []Line, prices []Price) ([]Data, error) {
Cost: int(dataCost / 1000.0), // compensate for cost specified per MB Cost: int(dataCost / 1000.0), // compensate for cost specified per MB
Price: int(dataPrice / 1000.0), // compensate for price specified per MB Price: int(dataPrice / 1000.0), // compensate for price specified per MB
}, },
Bytes: l.RawCount, Account: l.Account,
Kilobytes: l.RawCount,
} }
i++ i++
} }
@ -193,6 +195,7 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
*/ */
callCost := 0.0 callCost := 0.0
callPrice := 0.0 callPrice := 0.0
rawCost := 0
pricedLegs := make([]PricedLine, len(v)) pricedLegs := make([]PricedLine, len(v))
for i, leg := range v { for i, leg := range v {
legPrice := Price{} legPrice := Price{}
@ -247,10 +250,14 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
pricedLeg.Line = leg pricedLeg.Line = leg
pricedLeg.Cost = int(math.Round(thisLegCost)) pricedLeg.Cost = int(math.Round(thisLegCost))
pricedLeg.Price = int(math.Round(thisLegPrice)) pricedLeg.Price = int(math.Round(thisLegPrice))
if legPrice.OurRef != "" {
pricedLeg.OurRef = legPrice.OurRef
}
pricedLegs[i] = pricedLeg pricedLegs[i] = pricedLeg
callCost += thisLegCost callCost += thisLegCost
callPrice += thisLegPrice callPrice += thisLegPrice
rawCost += leg.RawCosts
if DEBUG { 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) 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)
} }
@ -259,11 +266,12 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
//fmt.Printf("(%12s->%12s) leg %d, price: %+v\n", leg.CLI, leg.To, leg.Leg, legPrice) //fmt.Printf("(%12s->%12s) leg %d, price: %+v\n", leg.CLI, leg.To, leg.Leg, legPrice)
} }
callDuration, _ := time.ParseDuration(fmt.Sprintf("%ds", v[0].RawCount)) callDuration, _ := time.ParseDuration(fmt.Sprintf("%ds", v[0].RawCount))
var callFrom, callTo string var callFrom, callTo, callDescription string
ret[i] = Call{ ret[i] = Call{
Duration: callDuration, Duration: callDuration,
Time: v[0].Time, Time: v[0].Time,
RawCost: rawCost,
Cost: int(math.Round(callCost)), Cost: int(math.Round(callCost)),
Price: int(math.Round(callPrice)), Price: int(math.Round(callPrice)),
Legs: pricedLegs, Legs: pricedLegs,
@ -272,11 +280,26 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
// CLI == From, then this leg's CLI is our "From" field, and this leg's "To" likewise // CLI == From, then this leg's CLI is our "From" field, and this leg's "To" likewise
callFrom = v[0].CLI callFrom = v[0].CLI
callTo = v[0].To callTo = v[0].To
if pricedLegs[0].OurRef == "" {
strippedDestination := strings.TrimSpace(strings.TrimSuffix(strings.TrimSuffix(pricedLegs[0].Line.Destination, " - Proper"), " - Mobile"))
callDescription = strippedDestination
} else {
callDescription = pricedLegs[0].OurRef
}
} else { } else {
// We capture the "To" from the leg that has no source, we capture the "From" from the leg that has both source and destination // We capture the "To" from the leg that has no source, we capture the "From" from the leg that has both source and destination
for _, leg := range v { for _, leg := range v {
if leg.Source == "" && leg.Destination != "" { if leg.Source == "" && leg.Destination != "" {
callTo = leg.To callTo = leg.To
strippedDestination := strings.TrimSpace(strings.TrimSuffix(strings.TrimSuffix(leg.Destination, " - Proper"), " - Mobile"))
switch strippedDestination {
case "Netherlands - Mobile":
callDescription = "Nederland (mobiel)"
case "Netherlands - Fixed":
callDescription = "Nederland (vast)"
default:
callDescription = strippedDestination
}
} else if leg.Source != "" && leg.Destination != "" { } else if leg.Source != "" && leg.Destination != "" {
callFrom = leg.From callFrom = leg.From
} else { } else {
@ -287,6 +310,8 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) {
} }
ret[i].From = callFrom ret[i].From = callFrom
ret[i].To = callTo ret[i].To = callTo
ret[i].Account = v[0].Account
ret[i].Description = callDescription
//fmt.Printf(" ==> %+v\n\n", ret[i]) //fmt.Printf(" ==> %+v\n\n", ret[i])
//ret[i].Legs = make([]PricedLine, len(v)) //ret[i].Legs = make([]PricedLine, len(v))

71
export_csv.go

@ -0,0 +1,71 @@
package cdr
import (
"fmt"
)
type ExportLineIndex int
var (
ExportLineSize = 9
ExportLineTypeCall = "CALL"
ExportLineTypeText = "TEXT"
ExportLineTypeData = "DATA"
ExportLineHeader = []string{
"Type", "Timestamp", "From", "To", "Units", "Account", "Cost", "Price", "Description",
}
)
const (
ExportLineType ExportLineIndex = iota
ExportLineTimestamp
ExportLineFrom
ExportLineTo
ExportLineUnits
ExportLineAccount
ExportLineCost
ExportLinePrice
ExportLineDescription
)
func ExportCall(c Call) []string {
ret := make([]string, ExportLineSize)
ret[ExportLineType] = ExportLineTypeCall
ret[ExportLineTimestamp] = c.Time.Format(DateTimeFormat)
ret[ExportLineFrom] = c.From
ret[ExportLineTo] = c.To
ret[ExportLineUnits] = fmt.Sprintf("%.0f", c.Duration.Seconds())
ret[ExportLineAccount] = c.Account
ret[ExportLineCost] = fmt.Sprintf("%d", c.Cost)
ret[ExportLinePrice] = fmt.Sprintf("%d", c.Price)
ret[ExportLineDescription] = c.Description
return ret
}
func ExportText(t Text) []string {
ret := make([]string, ExportLineSize)
ret[ExportLineType] = ExportLineTypeText
ret[ExportLineTimestamp] = t.Time.Format(DateTimeFormat)
ret[ExportLineFrom] = t.From
ret[ExportLineTo] = t.To
ret[ExportLineUnits] = "1"
ret[ExportLineAccount] = t.Account
ret[ExportLineCost] = fmt.Sprintf("%d", t.Cost)
ret[ExportLinePrice] = fmt.Sprintf("%d", t.Price)
ret[ExportLineDescription] = "SMS"
return ret
}
func ExportData(d Data) []string {
ret := make([]string, ExportLineSize)
ret[ExportLineType] = ExportLineTypeData
ret[ExportLineTimestamp] = d.Time.Format(DateTimeFormat)
ret[ExportLineFrom] = ""
ret[ExportLineTo] = ""
ret[ExportLineUnits] = fmt.Sprintf("%d", d.Kilobytes)
ret[ExportLineAccount] = d.Account
ret[ExportLineCost] = fmt.Sprintf("%d", d.PricedLine.Cost)
ret[ExportLinePrice] = fmt.Sprintf("%d", d.PricedLine.Price)
ret[ExportLineDescription] = "Data"
return ret
}

16
record.go

@ -123,6 +123,7 @@ type PricedLine struct {
Cost int Cost int
Price int Price int
OurRef string
} }
// Call is a record of one or more PricedLines that ultimately form "a call". // Call is a record of one or more PricedLines that ultimately form "a call".
@ -131,19 +132,27 @@ type Call struct {
To string To string
Time time.Time Time time.Time
Duration time.Duration Duration time.Duration
RawCost int
Cost int Cost int
Price int Price int
Kind CallKind Kind CallKind
Account string
Description string
Legs []PricedLine Legs []PricedLine
} }
func (c *Call) String() string { 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) diff := float64(c.RawCost-c.Cost) / 10000.0
return fmt.Sprintf("%s (%d) (%12s->%12s) cost: %.4f (%.4f=>%+.4f), price: %.4f (dur: %v): %v",
c.Time.Format(DateTimeFormat), len(c.Legs), c.From, c.To,
float64(c.Cost)/10000.0, float64(c.RawCost)/10000.0, diff, float64(c.Price)/10000.0, c.Duration, c.Description)
} }
// Text is a specific PricedLine for a text message. // Text is a specific PricedLine for a text message.
type Text struct { type Text struct {
PricedLine PricedLine
Account string
} }
func (t *Text) String() string { func (t *Text) String() string {
@ -154,9 +163,10 @@ func (t *Text) String() string {
type Data struct { type Data struct {
PricedLine PricedLine
Bytes int Account string
Kilobytes int
} }
func (d *Data) String() string { 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) return fmt.Sprintf("%s (%s) %v kilobytes, cost: %.4f, price: %.4f", d.PricedLine.Line.Time.Format(DateTimeFormat), d.Account, d.Kilobytes, float64(d.PricedLine.Cost)/10000.0, float64(d.PricedLine.Price)/10000.0)
} }

Loading…
Cancel
Save