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