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++ } // Now convert each text CDR to a priced Text ret := make([]Text, numTextCDRs) i := 0 for _, l := range cdrLines { if l.Kind != TextLine || l.Reason != ORIG { continue } ret[i] = Text{ PricedLine{ Line: l, Cost: TEXT_OUTGOING_BUY, Price: TEXT_OUTGOING_SELL, }, } 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) 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 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 } // Cache destination prices if _, ok := destinationToPrice[l.Destination]; !ok { priceLoop: for _, p := range prices { if p.Type != PriceCall { continue priceLoop } if p.Destination == l.Destination { 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: } } fmt.Printf("destinationPrice: %+v\n", destinationToPrice) */ return nil, fmt.Errorf("not yet implemented") }