|
|
|
@ -2,6 +2,7 @@ package cdr |
|
|
|
|
|
|
|
import ( |
|
|
|
"fmt" |
|
|
|
"math" |
|
|
|
"strings" |
|
|
|
"time" |
|
|
|
) |
|
|
|
@ -112,6 +113,9 @@ 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_FREEPHONE.Destination] = CALL_NATIONAL_DESTINATION_FREEPHONE |
|
|
|
destinationToPrice[CALL_NATIONAL_DESTINATION_PREMIUM_NOSETUP.Destination] = CALL_NATIONAL_DESTINATION_PREMIUM_NOSETUP |
|
|
|
destinationToPrice[CALL_NATIONAL_DESTINATION_VOICEMAIL.Destination] = CALL_NATIONAL_DESTINATION_VOICEMAIL |
|
|
|
|
|
|
|
nonMatchedDestinations := make(map[string]struct{}) |
|
|
|
for _, l := range cdrLines { |
|
|
|
@ -121,15 +125,22 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
|
// Cache destination prices
|
|
|
|
_, knownNonPriced := nonMatchedDestinations[l.Destination] |
|
|
|
if _, ok := destinationToPrice[l.Destination]; !ok && !knownNonPriced { |
|
|
|
matched := false |
|
|
|
priceLoop: |
|
|
|
for _, p := range prices { |
|
|
|
if p.Type != PriceCall { |
|
|
|
fmt.Printf("[debug] skipping this price: %+v\n", p) |
|
|
|
continue priceLoop |
|
|
|
} |
|
|
|
if p.Destination == l.Destination { |
|
|
|
destinationToPrice[l.Destination] = p |
|
|
|
matched = true |
|
|
|
break priceLoop |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
if !matched { |
|
|
|
fmt.Printf("[debug] non-matched price for destination %q\n", l.Destination) |
|
|
|
nonMatchedDestinations[l.Destination] = struct{}{} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -139,13 +150,13 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
|
fmt.Printf("[warning] no price for destination %v\n", k) |
|
|
|
} |
|
|
|
|
|
|
|
// a map from "%s_%s_%s": l.Time.Format(DateTimeFormat), l.CLI, l.From
|
|
|
|
// a map from "%s_%s_%s_%s": l.Time.Format(DateTimeFormat), l.CLI, l.From, l.Account
|
|
|
|
uniqueCalls := make(map[string][]Line) |
|
|
|
for _, l := range cdrLines { |
|
|
|
if l.Kind != VoiceLine { |
|
|
|
continue |
|
|
|
} |
|
|
|
idx := fmt.Sprintf("%s_%s_%s", l.Time.Format(DateTimeFormat), l.CLI, l.From) |
|
|
|
idx := fmt.Sprintf("%s_%s_%s_%s", l.Time.Format(DateTimeFormat), l.CLI, l.From, l.Account) |
|
|
|
if _, ok := uniqueCalls[idx]; !ok { |
|
|
|
uniqueCalls[idx] = make([]Line, 0) |
|
|
|
} |
|
|
|
@ -162,6 +173,8 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
|
} |
|
|
|
fmt.Println() |
|
|
|
*/ |
|
|
|
callCost := 0.0 |
|
|
|
callPrice := 0.0 |
|
|
|
for _, leg := range v { |
|
|
|
legPrice := Price{} |
|
|
|
if leg.CLI == leg.From { |
|
|
|
@ -188,6 +201,8 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
|
case leg.From == leg.To && leg.Source == CALL_TO_HANDSET_SOURCE && leg.Destination == CALL_TO_HANDSET_DESTINATION: |
|
|
|
// This leg consists of the airtime (PBX->HS, roaming + non-roaming)
|
|
|
|
legPrice = CALL_NATIONAL_DESTINATION_AIRTIME_TO_HANDSET |
|
|
|
case leg.Destination == CALL_TO_VOICEMAIL && (leg.Reason == CFNA || leg.Reason == CFBS || leg.Reason == CFIM): |
|
|
|
legPrice = CALL_NATIONAL_DESTINATION_VOICEMAIL |
|
|
|
// source -> HS (roaming)
|
|
|
|
case leg.Source == CALL_TO_HANDSET_SOURCE && leg.Reason == ROAM: |
|
|
|
// We only know the destination country
|
|
|
|
@ -202,23 +217,34 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
|
_, mapOk := destinationToPrice[leg.Destination] |
|
|
|
fmt.Printf("[warning] non-matched CDR leg (cid: %q, dt: %v, CLI: %s, %s->%s, src: %s, dest: %s (%s) %v\n", |
|
|
|
leg.Id, leg.Time.Format(DateTimeFormat), leg.CLI, leg.From, leg.To, leg.Source, leg.Destination, leg.Reason, mapOk) |
|
|
|
} else { |
|
|
|
// RawCount is in seconds. Buy/SellUnit is in cents*100, per minute
|
|
|
|
callCost += float64(legPrice.BuyEach) + float64(leg.RawCount)*(float64(legPrice.BuyUnit)/60.0) |
|
|
|
callPrice += float64(legPrice.SellEach) + float64(leg.RawCount)*(float64(legPrice.SellUnit)/60.0) |
|
|
|
fmt.Printf("(%12s->%12s) leg %d, callCost: %.4f, callPrice: %.4f (dst: %q)\n", leg.CLI, leg.To, leg.Leg, callCost, callPrice, 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) |
|
|
|
} |
|
|
|
ret[i] = Call{ |
|
|
|
From: "asdf", |
|
|
|
To: "asdkflj", |
|
|
|
Duration: time.Duration(0), |
|
|
|
Cost: 0, |
|
|
|
Price: 0, |
|
|
|
} |
|
|
|
/* |
|
|
|
ret[i].Legs = make([]PricedLine, len(v)) |
|
|
|
for j, leg := range v { |
|
|
|
ret[i].Legs[j] = leg |
|
|
|
//fmt.Printf("(%12s->%12s) leg %d, price: %+v\n", leg.CLI, leg.To, leg.Leg, legPrice)
|
|
|
|
} |
|
|
|
if len(v) == 1 { |
|
|
|
callDuration, _ := time.ParseDuration(fmt.Sprintf("%ds", v[0].RawCount)) |
|
|
|
// CLI == From, then this leg's CLI is our "From" field, and this leg's "To" likewise
|
|
|
|
ret[i] = Call{ |
|
|
|
From: v[0].CLI, |
|
|
|
To: v[0].To, |
|
|
|
Duration: callDuration, |
|
|
|
Time: v[0].Time, |
|
|
|
Cost: int(math.Round(callCost)), |
|
|
|
Price: int(math.Round(callPrice)), |
|
|
|
} |
|
|
|
*/ |
|
|
|
} else { |
|
|
|
|
|
|
|
} |
|
|
|
fmt.Printf("==> %+v\n", ret[i]) |
|
|
|
//ret[i].Legs = make([]PricedLine, len(v))
|
|
|
|
//for j, leg := range v {
|
|
|
|
//ret[i].Legs[j] = leg
|
|
|
|
//}
|
|
|
|
i++ |
|
|
|
} |
|
|
|
|
|
|
|
|