|
|
@ -3,6 +3,7 @@ package cdr |
|
|
import ( |
|
|
import ( |
|
|
"fmt" |
|
|
"fmt" |
|
|
"strings" |
|
|
"strings" |
|
|
|
|
|
"time" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
func CombineTextCDRs(cdrLines []Line, prices []Price) ([]Text, error) { |
|
|
func CombineTextCDRs(cdrLines []Line, prices []Price) ([]Text, error) { |
|
|
@ -112,12 +113,14 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
destinationToPrice[CALL_NATIONAL_DESTINATION_AIRTIME_FROM_HANDSET.Destination] = CALL_NATIONAL_DESTINATION_AIRTIME_FROM_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.Destination] = CALL_NATIONAL_DESTINATION_MOBILE |
|
|
|
|
|
|
|
|
|
|
|
nonMatchedDestinations := make(map[string]struct{}) |
|
|
for _, l := range cdrLines { |
|
|
for _, l := range cdrLines { |
|
|
if l.Kind != VoiceLine { |
|
|
if l.Kind != VoiceLine { |
|
|
continue |
|
|
continue |
|
|
} |
|
|
} |
|
|
// Cache destination prices
|
|
|
// Cache destination prices
|
|
|
if _, ok := destinationToPrice[l.Destination]; !ok { |
|
|
_, knownNonPriced := nonMatchedDestinations[l.Destination] |
|
|
|
|
|
if _, ok := destinationToPrice[l.Destination]; !ok && !knownNonPriced { |
|
|
priceLoop: |
|
|
priceLoop: |
|
|
for _, p := range prices { |
|
|
for _, p := range prices { |
|
|
if p.Type != PriceCall { |
|
|
if p.Type != PriceCall { |
|
|
@ -127,10 +130,15 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
destinationToPrice[l.Destination] = p |
|
|
destinationToPrice[l.Destination] = p |
|
|
break priceLoop |
|
|
break priceLoop |
|
|
} |
|
|
} |
|
|
|
|
|
nonMatchedDestinations[l.Destination] = struct{}{} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for k := range nonMatchedDestinations { |
|
|
|
|
|
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": l.Time.Format(DateTimeFormat), l.CLI, l.From
|
|
|
uniqueCalls := make(map[string][]Line) |
|
|
uniqueCalls := make(map[string][]Line) |
|
|
for _, l := range cdrLines { |
|
|
for _, l := range cdrLines { |
|
|
@ -144,43 +152,76 @@ func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
uniqueCalls[idx] = append(uniqueCalls[idx], l) |
|
|
uniqueCalls[idx] = append(uniqueCalls[idx], l) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for k, v := range uniqueCalls { |
|
|
ret := make([]Call, len(uniqueCalls)) |
|
|
fmt.Printf("[%s] %d\n", k, len(v)) |
|
|
i := 0 |
|
|
for i, c := range v { |
|
|
for _, v := range uniqueCalls { |
|
|
fmt.Printf("\t%d (%d) [%25s]: (%12s): %12s->%12s (%s)\n", i, c.Leg, c.Account, c.CLI, c.From, c.To, c.Reason) |
|
|
/* |
|
|
} |
|
|
fmt.Printf("[%s] %d\n", k, len(v)) |
|
|
fmt.Println() |
|
|
for i, c := range v { |
|
|
} |
|
|
fmt.Printf("\t%d (%d) [%25s]: (%12s): %12s->%12s (%s)\n", i, c.Leg, c.Account, c.CLI, c.From, c.To, c.Reason) |
|
|
|
|
|
|
|
|
/* |
|
|
|
|
|
ret := make([]Call, 0) |
|
|
|
|
|
for _, l := range cdrLines { |
|
|
|
|
|
if l.Kind != VoiceLine { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
fmt.Println() |
|
|
switch { |
|
|
*/ |
|
|
// HS -> PBX (non-roaming)
|
|
|
for _, leg := range v { |
|
|
case l.CLI == l.From && l.Source == CALL_FROM_HANDSET_SOURCE && l.Destination == CALL_FROM_HANDSET_DESTINATION: |
|
|
legPrice := Price{} |
|
|
|
|
|
if leg.CLI == leg.From { |
|
|
// PBX -> HS (roaming+non-roaming)
|
|
|
switch { |
|
|
case l.From == l.To && l.Source == CALL_TO_HANDSET_SOURCE && l.Destination == CALL_TO_HANDSET_DESTINATION: |
|
|
case leg.Source == CALL_FROM_HANDSET_SOURCE && leg.Destination == CALL_FROM_HANDSET_DESTINATION: |
|
|
|
|
|
// This leg consists of the airtime (outgoing call from the HS perspective) (HS->PBX, non-roaming)
|
|
|
// HS -> Regular (non-roaming)
|
|
|
legPrice = CALL_NATIONAL_DESTINATION_AIRTIME_FROM_HANDSET |
|
|
case l.CLI == l.From && l.Source == CALL_FROM_HANDSET_SOURCE: |
|
|
|
|
|
|
|
|
case leg.From != leg.To && leg.Source == CALL_FROM_HANDSET_SOURCE: // Case 2, 3
|
|
|
// HS -> Regular (roaming)
|
|
|
// This leg consists of routing a call (from a PBX or Handset) through our platform. The costs are defined by the call's destination.
|
|
|
case l.CLI == l.From && l.Source != CALL_FROM_HANDSET_SOURCE && l.Reason == ORIG: |
|
|
// Case 1: PBX->destination
|
|
|
|
|
|
// Case 2: HS->destination, non roaming
|
|
|
// Regular -> HS (roaming)
|
|
|
// Case 3: HS->destination, roaming
|
|
|
case l.CLI != l.From && l.Source == CALL_TO_HANDSET_SOURCE && l.Reason == ROAM: |
|
|
// Case 4: HS->PBX, roaming
|
|
|
|
|
|
fallthrough |
|
|
// Regular -> HS (non-roaming) does not exist in the CSV
|
|
|
case leg.Source != CALL_FROM_HANDSET_SOURCE && leg.Reason == ORIG: // Case 1
|
|
|
default: |
|
|
fallthrough |
|
|
|
|
|
case leg.From != leg.To && leg.Source == CALL_FROM_PBX_SOURCE: // Case 4
|
|
|
|
|
|
legPrice = destinationToPrice[leg.Destination] |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if leg.CLI != leg.From { |
|
|
|
|
|
switch { |
|
|
|
|
|
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 |
|
|
|
|
|
// source -> HS (roaming)
|
|
|
|
|
|
case leg.Source == CALL_TO_HANDSET_SOURCE && leg.Reason == ROAM: |
|
|
|
|
|
// We only know the destination country
|
|
|
|
|
|
fallthrough |
|
|
|
|
|
case leg.CLI == CALL_CLI_ANONYMOUS: |
|
|
|
|
|
// Incoming call from a blocked CLI
|
|
|
|
|
|
legPrice = destinationToPrice[leg.Destination] |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if legPrice == (Price{}) { |
|
|
|
|
|
// error, weird category
|
|
|
|
|
|
_, 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) |
|
|
|
|
|
} |
|
|
|
|
|
// 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 |
|
|
|
|
|
} |
|
|
|
|
|
*/ |
|
|
|
|
|
i++ |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
fmt.Printf("destinationPrice: %+v\n", destinationToPrice) |
|
|
fmt.Printf("destinationPrice: %+v\n", destinationToPrice) |
|
|
*/ |
|
|
|
|
|
return nil, fmt.Errorf("not yet implemented") |
|
|
return nil, fmt.Errorf("not yet implemented") |
|
|
} |
|
|
} |
|
|
|