|
|
|
@ -2,14 +2,21 @@ package cdr |
|
|
|
|
|
|
|
import ( |
|
|
|
"fmt" |
|
|
|
"strings" |
|
|
|
) |
|
|
|
|
|
|
|
func CombineTextCDRs(cdrLines []Line, prices []Price) ([]Text, error) { |
|
|
|
var err []string |
|
|
|
|
|
|
|
numTextCDRs := 0 |
|
|
|
for _, l := range cdrLines { |
|
|
|
if l.Kind != TextLine { |
|
|
|
continue |
|
|
|
} |
|
|
|
if l.Reason != ORIG { |
|
|
|
err = append(err, fmt.Sprintf("text CDR did not match reason ORIG: %+v", l)) |
|
|
|
continue |
|
|
|
} |
|
|
|
numTextCDRs++ |
|
|
|
} |
|
|
|
|
|
|
|
@ -17,7 +24,7 @@ func CombineTextCDRs(cdrLines []Line, prices []Price) ([]Text, error) { |
|
|
|
ret := make([]Text, numTextCDRs) |
|
|
|
i := 0 |
|
|
|
for _, l := range cdrLines { |
|
|
|
if l.Kind != TextLine { |
|
|
|
if l.Kind != TextLine || l.Reason != ORIG { |
|
|
|
continue |
|
|
|
} |
|
|
|
ret[i] = Text{ |
|
|
|
@ -30,41 +37,150 @@ func CombineTextCDRs(cdrLines []Line, prices []Price) ([]Text, error) { |
|
|
|
i++ |
|
|
|
} |
|
|
|
|
|
|
|
if len(err) > 0 { |
|
|
|
return nil, fmt.Errorf("error pricing text CDRs: %v", strings.Join(err, ", ")) |
|
|
|
} |
|
|
|
|
|
|
|
return ret, nil |
|
|
|
} |
|
|
|
|
|
|
|
func CombineDataCDRs(cdrLines []Line, prices []Price) ([]Data, error) { |
|
|
|
var err []string |
|
|
|
numDataCDRs := 0 |
|
|
|
errSet := false |
|
|
|
for _, l := range cdrLines { |
|
|
|
errSet = false |
|
|
|
if l.Kind != DataLine { |
|
|
|
continue |
|
|
|
} |
|
|
|
if l.Reason != ORIG { |
|
|
|
err = append(err, fmt.Sprintf("data CDR did not match reason ORIG: %+v", l)) |
|
|
|
errSet = true |
|
|
|
} |
|
|
|
if l.Destination != DATA_EXPECTED_DESTINATION { |
|
|
|
err = append(err, fmt.Sprintf("data CDR did not match expected destination %q: %+v", DATA_EXPECTED_DESTINATION, l)) |
|
|
|
errSet = true |
|
|
|
} |
|
|
|
if errSet { |
|
|
|
continue |
|
|
|
} |
|
|
|
numDataCDRs++ |
|
|
|
} |
|
|
|
|
|
|
|
ret := make([]Data, numDataCDRs) |
|
|
|
return ret, fmt.Errorf("not yet implemented") |
|
|
|
i := 0 |
|
|
|
for _, l := range cdrLines { |
|
|
|
if l.Kind != DataLine || l.Reason != ORIG || l.Destination != DATA_EXPECTED_DESTINATION { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
// Initially set cost and price to national buy/sell values
|
|
|
|
dataCost := DATA_OUTGOING_NATIONAL_BUY |
|
|
|
dataPrice := DATA_OUTGOING_NATIONAL_SELL |
|
|
|
if l.Source != DATA_NATIONAL_SOURCE { |
|
|
|
// Roaming CDR
|
|
|
|
// TODO: distinguish between Europe "Roam-like-at-home" and worldwide
|
|
|
|
dataCost = DATA_OUTGOING_ROAMING_BUY |
|
|
|
dataPrice = DATA_OUTGOING_ROAMING_SELL |
|
|
|
} |
|
|
|
dataBytes := l.RawCount |
|
|
|
dataCost *= dataBytes |
|
|
|
dataPrice *= dataBytes |
|
|
|
|
|
|
|
ret[i] = Data{ |
|
|
|
PricedLine: PricedLine{ |
|
|
|
Line: l, |
|
|
|
Cost: int(dataCost / 1000.0), // compensate for cost specified per MB
|
|
|
|
Price: int(dataPrice / 1000.0), // compensate for price specified per MB
|
|
|
|
}, |
|
|
|
Bytes: l.RawCount, |
|
|
|
} |
|
|
|
i++ |
|
|
|
} |
|
|
|
|
|
|
|
if len(err) > 0 { |
|
|
|
return nil, fmt.Errorf("error pricing data CDRs: %v", strings.Join(err, ", ")) |
|
|
|
} |
|
|
|
return ret, nil |
|
|
|
} |
|
|
|
|
|
|
|
func CombineCallCDRs(cdrLines []Line, prices []Price) ([]Call, error) { |
|
|
|
// Preprocess the price list into a map of "destination" -> Price, filter out the Text destinations, and count how many Text CDRs we have
|
|
|
|
destinationToPriceLine := make(map[string]Price) |
|
|
|
numCallCDRs := 0 |
|
|
|
// Preprocess the price list into a map of "destination" -> Price, filter out the Voice destinations
|
|
|
|
destinationToPrice := make(map[string]Price) |
|
|
|
destinationToPrice[CALL_NATIONAL_DESTINATION_FIXED.Destination] = CALL_NATIONAL_DESTINATION_FIXED |
|
|
|
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 |
|
|
|
|
|
|
|
for _, l := range cdrLines { |
|
|
|
if l.Kind != VoiceLine { |
|
|
|
continue |
|
|
|
} |
|
|
|
if _, ok := destinationToPriceLine[l.Destination]; !ok { |
|
|
|
// Cache destination prices
|
|
|
|
if _, ok := destinationToPrice[l.Destination]; !ok { |
|
|
|
priceLoop: |
|
|
|
for _, p := range prices { |
|
|
|
if p.Type != PriceCall { |
|
|
|
continue |
|
|
|
continue priceLoop |
|
|
|
} |
|
|
|
if p.Destination == l.Destination { |
|
|
|
destinationToPriceLine[l.Destination] = p |
|
|
|
break |
|
|
|
destinationToPrice[l.Destination] = p |
|
|
|
break priceLoop |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// a map from "%s_%s_%s": l.Time.Format(DateTimeFormat), l.CLI, l.From
|
|
|
|
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) |
|
|
|
if _, ok := uniqueCalls[idx]; !ok { |
|
|
|
uniqueCalls[idx] = make([]Line, 0) |
|
|
|
} |
|
|
|
uniqueCalls[idx] = append(uniqueCalls[idx], l) |
|
|
|
} |
|
|
|
|
|
|
|
for k, v := range uniqueCalls { |
|
|
|
fmt.Printf("[%s] %d\n", k, len(v)) |
|
|
|
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) |
|
|
|
} |
|
|
|
fmt.Println() |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
ret := make([]Call, 0) |
|
|
|
for _, l := range cdrLines { |
|
|
|
if l.Kind != VoiceLine { |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
switch { |
|
|
|
// HS -> PBX (non-roaming)
|
|
|
|
case l.CLI == l.From && l.Source == CALL_FROM_HANDSET_SOURCE && l.Destination == CALL_FROM_HANDSET_DESTINATION: |
|
|
|
|
|
|
|
// PBX -> HS (roaming+non-roaming)
|
|
|
|
case l.From == l.To && l.Source == CALL_TO_HANDSET_SOURCE && l.Destination == CALL_TO_HANDSET_DESTINATION: |
|
|
|
|
|
|
|
// HS -> Regular (non-roaming)
|
|
|
|
case l.CLI == l.From && l.Source == CALL_FROM_HANDSET_SOURCE: |
|
|
|
|
|
|
|
// HS -> Regular (roaming)
|
|
|
|
case l.CLI == l.From && l.Source != CALL_FROM_HANDSET_SOURCE && l.Reason == ORIG: |
|
|
|
|
|
|
|
// Regular -> HS (roaming)
|
|
|
|
case l.CLI != l.From && l.Source == CALL_TO_HANDSET_SOURCE && l.Reason == ROAM: |
|
|
|
|
|
|
|
// Regular -> HS (non-roaming) does not exist in the CSV
|
|
|
|
default: |
|
|
|
} |
|
|
|
numCallCDRs++ |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Printf("destinationPrice: %+v\n", destinationToPrice) |
|
|
|
*/ |
|
|
|
return nil, fmt.Errorf("not yet implemented") |
|
|
|
} |
|
|
|
|