diff options
Diffstat (limited to 'pkg/adapters/xlsx')
| -rw-r--r-- | pkg/adapters/xlsx/model.go | 60 | ||||
| -rw-r--r-- | pkg/adapters/xlsx/registry.go | 91 | ||||
| -rw-r--r-- | pkg/adapters/xlsx/xlsx.go | 84 |
3 files changed, 154 insertions, 81 deletions
diff --git a/pkg/adapters/xlsx/model.go b/pkg/adapters/xlsx/model.go index d8c5194..79434f0 100644 --- a/pkg/adapters/xlsx/model.go +++ b/pkg/adapters/xlsx/model.go @@ -1,13 +1,13 @@ package xlsx import ( + "airlines/pkg/model" "errors" - "regexp" "strconv" "strings" "time" - "github.com/leonm1/airports-go" + "airlines/pkg/airports" ) type Ticket struct { @@ -16,9 +16,13 @@ type Ticket struct { Title string FlightNumber string FromCity string - ToCity string + FromCountry string FromAirport string + FromCoords model.LatLong + ToCity string + ToCountry string ToAirport string + ToCoords model.LatLong FlightDate string // (raw, expected YYYY-MM-DD; Excel text may start with ') FlightTime string // (raw, expected HH-MM or HH:MM; Excel text may start with ') PNR string @@ -87,52 +91,4 @@ func two(x int) string { return "0" + strconv.Itoa(x) } return strconv.Itoa(x) -} - -func parseCardLine(s string) (prefix string, number uint64, bonus string) { - raw := strings.TrimSpace(s) - if raw == "" { - return "", 0, "" - } - // number = last run of digits - if m := regexp.MustCompile(`(\d{3,})\D*$`).FindStringSubmatch(raw); len(m) == 2 { - if n, err := strconv.ParseUint(m[1], 10, 64); err == nil { - number = n - } - } - - // tokens (letters with '-', '/', apostrophes) - tokRe := regexp.MustCompile(`[A-Za-z][A-Za-z'/-]*`) - toks := tokRe.FindAllString(s, -1) - - // prefix = first 2–3 letter all-caps-ish token - for _, t := range toks { - u := strings.ToUpper(t) - if len(u) >= 2 && len(u) <= 3 && regexp.MustCompile(`^[A-Z]{2,3}$`).MatchString(u) { - prefix = u - break - } - } - // bonus = all tokens except prefix - words := []string{} - for _, t := range toks { - if strings.ToUpper(t) == prefix { - continue - } - words = append(words, t) - } - if len(words) > 0 { - bonus = strings.Join(words, " ") - } - if bonus == "" && prefix != "" { - bonus = prefix - } - return -} - -func firstNonEmpty(a, b string) string { - if strings.TrimSpace(a) != "" { - return a - } - return b -} +}
\ No newline at end of file diff --git a/pkg/adapters/xlsx/registry.go b/pkg/adapters/xlsx/registry.go index 46c395e..6b98c0b 100644 --- a/pkg/adapters/xlsx/registry.go +++ b/pkg/adapters/xlsx/registry.go @@ -1,69 +1,102 @@ package xlsx import ( + "errors" "fmt" + "strconv" "strings" + "time" "airlines/pkg/model" "airlines/pkg/names" - "github.com/leonm1/airports-go" + "airlines/pkg/airports" ) -func (t Ticket) ToUser() (model.User, error) { +func (t Ticket) ToUser() (*model.User, error) { fio, err := names.ParseLatinName(t.Passenger) if err != nil { - return model.User{}, fmt.Errorf("%v %s", t.Sheet, err.Error()) + return nil, fmt.Errorf("%v %s", t.Sheet, err.Error()) } sex := names.GenderFromTitle(t.Title) - u := model.User{ + u := &model.User{ Nick: "", - Name: fio.First, - Surname: fio.Last, - Fathersname: fio.Patronymic, + Name: strings.ToUpper(fio.First), + Surname: strings.ToUpper(fio.Last), + Fathersname: strings.ToUpper(fio.Patronymic), Sex: sex, } return u, nil } -func (t Ticket) ToCard() (model.Card, error) { - prefix, number, bonus := parseCardLine(t.Card) +func (t Ticket) ToCard() (*model.Card, error) { + prefix, number, bonus := model.ParseCardLine(t.Card) if number == 0 && prefix == "" && bonus == "" { - return model.Card{}, nil + return nil, errors.New("do not have card") } - return model.Card{ + return &model.Card{ Prefix: prefix, Number: number, Bonusprogramm: "", }, nil } -func (t Ticket) ToFlight() (model.Flight, error) { - // Resolve IATA records +func (t Ticket) ToFlight() (*model.Flight, error) { fromIATA := strings.ToUpper(strings.TrimSpace(t.FromAirport)) toIATA := strings.ToUpper(strings.TrimSpace(t.ToAirport)) fromRec, _ := airports.LookupIATA(fromIATA) - toRec, _ := airports.LookupIATA(toIATA) - fromCity := firstNonEmpty(strings.TrimSpace(t.FromCity), fromRec.City) - toCity := firstNonEmpty(strings.TrimSpace(t.ToCity), toRec.City) + dateStr := strings.TrimLeft(strings.TrimSpace(t.FlightDate), "'") + timeStr := strings.TrimLeft(strings.TrimSpace(t.FlightTime), "'") + timeStr = strings.ReplaceAll(timeStr, "-", ":") - fromCountry := fromRec.Country - toCountry := toRec.Country - departUTC, _, err := t.DateTime() + if dateStr == "" || timeStr == "" { + return nil, errors.New("missing FlightDate or FlightTime") + } + + hh, mm, err := parseHHMM(timeStr) + if err != nil { + return nil, err + } + + day, err := time.Parse("2006-01-02", dateStr) if err != nil { - return model.Flight{}, err + return nil, err } - return model.Flight{ - Number: strings.TrimSpace(t.FlightNumber), - From: fromIATA, - FromCity: fromCity, - FromCountry: fromCountry, - To: toIATA, - ToCity: toCity, - ToCountry: toCountry, - Date: departUTC, + + loc := model.TzFromAirportRecord(fromRec) + departLocal := time.Date(day.Year(), day.Month(), day.Day(), hh, mm, 0, 0, loc) + departUTC := departLocal.UTC() + + return &model.Flight{ + Number: strings.TrimSpace(t.FlightNumber), + From: fromIATA, + FromCoords: t.FromCoords, + To: toIATA, + ToCoords: t.ToCoords, + Date: departUTC, + HasTime: true, }, nil } + +func parseHHMM(s string) (int, int, error) { + // Accept "H:MM", "HH:MM" + parts := strings.Split(s, ":") + if len(parts) != 2 { + return 0, 0, errors.New("invalid FlightTime, expected HH:MM") + } + hh, err := strconv.Atoi(parts[0]) + if err != nil { + return 0, 0, err + } + mm, err := strconv.Atoi(parts[1]) + if err != nil { + return 0, 0, err + } + if hh < 0 || hh > 23 || mm < 0 || mm > 59 { + return 0, 0, errors.New("invalid FlightTime range") + } + return hh, mm, nil +} diff --git a/pkg/adapters/xlsx/xlsx.go b/pkg/adapters/xlsx/xlsx.go index 6ef9baa..5a8427b 100644 --- a/pkg/adapters/xlsx/xlsx.go +++ b/pkg/adapters/xlsx/xlsx.go @@ -2,8 +2,14 @@ package xlsx import ( "fmt" + "os" "strings" + "sync" + "airlines/pkg/airports" + "airlines/pkg/model" + + "github.com/schollz/progressbar/v3" "github.com/xuri/excelize/v2" ) @@ -84,7 +90,85 @@ func UnmarshallXlsxFile(fname string) ([]Ticket, error) { return nil, err } + ap, _ := airports.LookupIATA(t.FromAirport) + t.FromCountry = ap.Country + t.FromCoords.Lat = ap.Latitude + t.FromCoords.Long = ap.Longitude + + ap, _ = airports.LookupIATA(t.ToAirport) + t.ToCountry = ap.Country + t.ToCoords.Lat = ap.Latitude + t.ToCoords.Long = ap.Longitude + tickets = append(tickets, t) } return tickets, nil } + +func UnmarshallXlsxFiles(baseDir string) ([]Ticket, error) { + + tickets := make([]Ticket, 0) + items, err := os.ReadDir(baseDir) + if err != nil { + panic(err) + } + + bar := progressbar.Default(int64(len(items)), "unmarshalling xlsx") + + var mu sync.Mutex + var wg sync.WaitGroup + sem := make(chan struct{}, 8) + for _, item := range items { + if !item.IsDir() { + wg.Add(1) + sem <- struct{}{} + go func(name string) { + defer func() { <-sem }() + defer wg.Done() + // fmt.Println("Processing file:", name) + parsedTickets, err := UnmarshallXlsxFile(baseDir + name) + if err != nil { + panic(err) + } + mu.Lock() + defer mu.Unlock() + tickets = append(tickets, parsedTickets...) + bar.Add(1) + }(item.Name()) + } + } + wg.Wait() + + return tickets, nil +} + +func DumpToDb(store model.Store, tickets []Ticket) { + var err error + bar := progressbar.Default(int64(len(tickets)), "dumping xlsx") + for _, ticket := range tickets { + bar.Add(1) + dbUser, _ := ticket.ToUser() + dbUser, err = store.SaveUser(dbUser) + if err != nil { + panic(err) + } + + dbCard, err := ticket.ToCard() + if err != nil { + goto processflight + } + dbCard.UserID = dbUser.ID + _, err = store.SaveCard(dbCard) + // данные говно + if err != nil { + fmt.Println(err) + } + processflight: + dbFlight, _ := ticket.ToFlight() + dbFlight.UserID = dbUser.ID + _, err = store.SaveFlight(dbFlight) + if err != nil { + fmt.Println(err) + } + } +} |
