4 changed files with 328 additions and 4 deletions
@ -0,0 +1,68 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"flag" |
|||
"fmt" |
|||
"log" |
|||
"os" |
|||
|
|||
"src.wolkict.net/cdr" |
|||
) |
|||
|
|||
var ( |
|||
csvFile = flag.String("csv", "", "use this call detail record CSV file") |
|||
validate = flag.Bool("validate", false, "only validate the call detail record CSV, perform no other actions") |
|||
pricesFile = flag.String("prices", "", "use this pricing CSV file") |
|||
) |
|||
|
|||
func usage() { |
|||
fmt.Println("Usage: cdrtool -csv <file.csv> [-prices <file.csv>] [-validate]") |
|||
flag.PrintDefaults() |
|||
} |
|||
|
|||
func main() { |
|||
flag.Parse() |
|||
|
|||
if *csvFile == "" { |
|||
log.Fatalf("mandatory -csv not set") |
|||
} |
|||
|
|||
if _, err := os.Open(*csvFile); err != nil { |
|||
log.Fatalf("cannot access CSV %q: %v", *csvFile, err) |
|||
} |
|||
if _, err := os.Open(*pricesFile); err != nil { |
|||
log.Printf("[warning] cannot access prices file %q", *pricesFile) |
|||
} |
|||
|
|||
imported, err := cdr.ImportCSV(*csvFile) |
|||
if *validate { |
|||
if err != nil { |
|||
log.Fatalf("[validate] importing %q failed: %v", *csvFile, err) |
|||
} |
|||
log.Printf("[validate] importing %q succeeded", *csvFile) |
|||
return |
|||
} |
|||
if err != nil { |
|||
log.Fatalf("importing CSV failed: %v", err) |
|||
} |
|||
|
|||
importedPrices, err := cdr.ImportPricesFile(*pricesFile) |
|||
if *validate { |
|||
if err != nil { |
|||
log.Fatalf("[validate] importing %q failed: %v", *pricesFile, err) |
|||
} |
|||
log.Printf("[validate] importing %q succeeded", *pricesFile) |
|||
return |
|||
} |
|||
if err != nil { |
|||
log.Fatalf("importing CSV failed: %v", err) |
|||
} |
|||
|
|||
for _, i := range imported { |
|||
fmt.Printf("l: %+v\n", i) |
|||
} |
|||
|
|||
for _, i := range importedPrices { |
|||
fmt.Printf("p: %+v\n", i) |
|||
} |
|||
} |
|||
@ -0,0 +1,139 @@ |
|||
package cdr |
|||
|
|||
import ( |
|||
"encoding/csv" |
|||
"fmt" |
|||
"os" |
|||
"regexp" |
|||
) |
|||
|
|||
type PriceType int |
|||
|
|||
const ( |
|||
PriceUnknown PriceType = iota |
|||
PriceCall |
|||
PriceText |
|||
PriceData |
|||
) |
|||
|
|||
type Price struct { |
|||
Type PriceType |
|||
BuyEach int |
|||
BuyUnit int |
|||
SellEach int |
|||
SellUnit int |
|||
Destination string |
|||
} |
|||
|
|||
const FieldsPerPricingLine = 14 |
|||
|
|||
// PriceImportIndex keeps track of what column corresponds to what data
|
|||
type PriceImportIndex int |
|||
|
|||
const ( |
|||
PriceImportRegion PriceImportIndex = iota |
|||
PriceImportDestination |
|||
PriceImportPpcBuy |
|||
PriceImportPpmBuyFixed |
|||
PriceImportPpmBuyMobile |
|||
PriceImportPpcSell |
|||
PriceImportPpmSellFixed |
|||
PriceImportPpmSellMobile |
|||
PriceImportPpcMargin |
|||
PriceImportPpmMarginFixed |
|||
PriceImportPpmMarginMobile |
|||
PriceImportPpcSellVAT |
|||
PriceImportPpmSellFixedVAT |
|||
PriceImportPpmSellMobileVAT |
|||
) |
|||
|
|||
var ( |
|||
PricingRegexp = regexp.MustCompile(`(\d+\.\d+)`) |
|||
) |
|||
|
|||
// ImportPricesFile expects a CSV with the following fields:
|
|||
// - Regio (string)
|
|||
// - Destination (string)
|
|||
// - ppc_buy
|
|||
// - ppm_buy_fixed
|
|||
// - ppm_buy_mobile
|
|||
// - ppc_sell
|
|||
// - ppm_sell_fixed
|
|||
// - ppm_sell_mobile
|
|||
// - ppc_margin
|
|||
// - ppm_margin_fixed
|
|||
// - ppm_margin_mobile
|
|||
// - ppc_sell_vat
|
|||
// - ppm_sell_fixed_vat
|
|||
// - ppm_sell_mobile_vat
|
|||
|
|||
func ImportPricesFile(fn string) ([]Price, error) { |
|||
f, err := os.Open(fn) |
|||
if err != nil { |
|||
return nil, fmt.Errorf("opening file %q failed: %v", fn, err) |
|||
} |
|||
|
|||
csvReader := csv.NewReader(f) |
|||
csvReader.FieldsPerRecord = FieldsPerPricingLine |
|||
csvReader.Comma = ';' |
|||
|
|||
all, err := csvReader.ReadAll() |
|||
if err != nil { |
|||
return nil, fmt.Errorf("reading csv %q failed: %v", fn, err) |
|||
} |
|||
|
|||
var ret []Price |
|||
priceTypes := []string{ |
|||
"Fixed", |
|||
"Mobile", |
|||
} |
|||
for _, line := range all { |
|||
if line[PriceImportRegion] == "Regio" || line[PriceImportDestination] == "" { |
|||
// Header line or no destination
|
|||
continue |
|||
} |
|||
|
|||
for _, priceType := range priceTypes { |
|||
destination := line[PriceImportRegion] + " - " + line[PriceImportDestination] + " - " + priceType |
|||
priceKind := PriceCall // TODO: import Text and Data through the same CSV
|
|||
|
|||
var buyUnit, sellUnit int |
|||
buyEach := convertImportedPrice(line[PriceImportPpcBuy]) |
|||
sellEach := convertImportedPrice(line[PriceImportPpcSell]) |
|||
switch priceType { |
|||
case "Fixed": |
|||
buyUnit = convertImportedPrice(line[PriceImportPpmBuyFixed]) |
|||
sellUnit = convertImportedPrice(line[PriceImportPpmSellFixed]) |
|||
case "Mobile": |
|||
buyUnit = convertImportedPrice(line[PriceImportPpmBuyMobile]) |
|||
sellUnit = convertImportedPrice(line[PriceImportPpmSellMobile]) |
|||
default: |
|||
return nil, fmt.Errorf("unsupported priceType %q", priceType) |
|||
} |
|||
|
|||
// Extract the prices
|
|||
ret = append(ret, Price{ |
|||
Type: priceKind, |
|||
Destination: destination, |
|||
BuyEach: buyEach, |
|||
BuyUnit: buyUnit, |
|||
SellEach: sellEach, |
|||
SellUnit: sellUnit, |
|||
}) |
|||
} |
|||
} |
|||
return ret, nil |
|||
} |
|||
|
|||
func convertImportedPrice(s string) int { |
|||
if s == "" { |
|||
return 0 |
|||
} |
|||
|
|||
p := PricingRegexp.FindString(s) |
|||
conv, err := decimalStrToInt(p, ".") |
|||
if err != nil { |
|||
panic("cannot convert ImportedPrice: " + err.Error()) |
|||
} |
|||
return conv |
|||
} |
|||
Loading…
Reference in new issue