diff options
| author | leshe4ka46 <alex9102naid1@ya.ru> | 2025-10-27 20:36:28 +0300 |
|---|---|---|
| committer | leshe4ka46 <alex9102naid1@ya.ru> | 2025-10-28 13:42:21 +0300 |
| commit | bb833561aa74f02970aee13cdc75973b29716491 (patch) | |
| tree | 0914668e11dbf825979f7419ce1bc78294cd3f7f /pkg/model | |
| parent | e17a425dfb3382310fb5863f516dacdca9f44956 (diff) | |
# This is a combination of 2 commits.
# This is the 1st commit message:
unmarshal all formats, merge them in the single table, users are truly unique
# This is the commit message #2:
i
Diffstat (limited to 'pkg/model')
| -rw-r--r-- | pkg/model/card.go | 56 | ||||
| -rw-r--r-- | pkg/model/store.go | 7 | ||||
| -rw-r--r-- | pkg/model/types.go | 74 | ||||
| -rw-r--r-- | pkg/model/user.go | 66 |
4 files changed, 156 insertions, 47 deletions
diff --git a/pkg/model/card.go b/pkg/model/card.go new file mode 100644 index 0000000..6ed666b --- /dev/null +++ b/pkg/model/card.go @@ -0,0 +1,56 @@ +package model + +import ( + "regexp" + "strconv" + "strings" +) + +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 +} diff --git a/pkg/model/store.go b/pkg/model/store.go new file mode 100644 index 0000000..8479de6 --- /dev/null +++ b/pkg/model/store.go @@ -0,0 +1,7 @@ +package model + +type Store interface { + SaveUser(u *User) (*User, error) + SaveCard(c *Card) (*Card, error) + SaveFlight(f *Flight) (*Flight, error) +} diff --git a/pkg/model/types.go b/pkg/model/types.go new file mode 100644 index 0000000..fd65d46 --- /dev/null +++ b/pkg/model/types.go @@ -0,0 +1,74 @@ +package model + +import ( + "encoding/json" + "math" + "strconv" + "strings" + "time" + + "airlines/pkg/airports" +) + +type Sex uint8 + +const ( + SexUnknown Sex = 0 + SexMale Sex = 1 + SexFemale Sex = 2 +) + +func (s *Sex) UnmarshalJSON(b []byte) error { + var raw string + if err := json.Unmarshal(b, &raw); err == nil { + switch strings.ToLower(strings.TrimSpace(raw)) { + case "male": + *s = SexMale + return nil + case "female": + *s = SexFemale + return nil + case "", "unknown", "null": + *s = SexUnknown + return nil + } + } + // also accept numbers in JSON + var n int + if err := json.Unmarshal(b, &n); err == nil { + *s = Sex(n) + return nil + } + *s = SexUnknown + return nil +} + +func TzFromAirportRecord(a airports.Airport) *time.Location { + return TzFromOffsetHours(a.Timezone) +} + +func TzFromOffsetHours(hours float64) *time.Location { + minTotal := int(math.Round(hours * 60.0)) + sec := minTotal * 60 + + name := fixedZoneNameFromMinutes(minTotal) + return time.FixedZone(name, sec) +} + +func fixedZoneNameFromMinutes(minTotal int) string { + sign := "+" + if minTotal < 0 { + sign = "-" + minTotal = -minTotal + } + h := minTotal / 60 + m := minTotal % 60 + return "UTC" + sign + two(h) + ":" + two(m) +} + +func two(x int) string { + if x < 10 { + return "0" + strconv.Itoa(x) + } + return strconv.Itoa(x) +} diff --git a/pkg/model/user.go b/pkg/model/user.go index bfa3694..213934a 100644 --- a/pkg/model/user.go +++ b/pkg/model/user.go @@ -1,48 +1,11 @@ package model import ( - "encoding/json" - "strings" "time" ) -const sentinelYear = 1 - func SentinelBirthday() time.Time { - return time.Date(sentinelYear, 1, 1, 0, 0, 0, 0, time.UTC) -} - -type Sex uint8 - -const ( - SexUnknown Sex = 0 - SexMale Sex = 1 - SexFemale Sex = 2 -) - -func (s *Sex) UnmarshalJSON(b []byte) error { - var raw string - if err := json.Unmarshal(b, &raw); err == nil { - switch strings.ToLower(strings.TrimSpace(raw)) { - case "male": - *s = SexMale - return nil - case "female": - *s = SexFemale - return nil - case "", "unknown", "null": - *s = SexUnknown - return nil - } - } - // also accept numbers in JSON - var n int - if err := json.Unmarshal(b, &n); err == nil { - *s = Sex(n) - return nil - } - *s = SexUnknown - return nil + return time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) } type User struct { @@ -58,9 +21,11 @@ type User struct { Birthday time.Time Cards []Card `gorm:"foreignKey:UserID"` + CardsIDs []uint64 `gorm:"-"` // just for compatibility Flights []Flight `gorm:"many2many:user_flights;joinForeignKey:UserID;joinReferences:FlightID"` + FlightsIDs []uint64 `gorm:"-"` } func (User) TableName() string { return "users" } @@ -73,8 +38,8 @@ type Card struct { Bonusprogramm string `gorm:"not null;uniqueIndex:uniq_card_identity"` - // User has multiple cards -> each card has registered flights to it Flights []Flight `gorm:"many2many:card_flights;joinForeignKey:CardID;joinReferences:FlightID"` + FlightsIDs []uint64 `gorm:"-"` UserID uint64 User User `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` @@ -82,22 +47,29 @@ type Card struct { func (Card) TableName() string { return "cards" } +type LatLong struct { + Lat, Long float64 +} + type Flight struct { ID uint64 `gorm:"primaryKey"` - Number string `gorm:"not null;uniqueIndex:uniq_flight_identity"` - From string `gorm:"not null;uniqueIndex:uniq_flight_identity"` - FromCity string `gorm:"not null;uniqueIndex:uniq_flight_identity"` - FromCountry string `gorm:"not null;uniqueIndex:uniq_flight_identity"` + Number string `gorm:"not null;uniqueIndex:uniq_flight_identity"` + From string `gorm:"not null;uniqueIndex:uniq_flight_identity"` + FromCoords LatLong `gorm:"-"` + + To string `gorm:"not null;uniqueIndex:uniq_flight_identity"` + ToCoords LatLong `gorm:"-"` - To string `gorm:"not null;uniqueIndex:uniq_flight_identity"` - ToCity string `gorm:"not null;uniqueIndex:uniq_flight_identity"` - ToCountry string `gorm:"not null;uniqueIndex:uniq_flight_identity"` + Date time.Time `gorm:"not null;uniqueIndex:uniq_flight_identity"` + HasTime bool - Date time.Time `gorm:"not null;uniqueIndex:uniq_flight_identity"` + Code string `gorm:"-"` Users []User `gorm:"many2many:user_flights;joinForeignKey:FlightID;joinReferences:UserID"` Cards []Card `gorm:"many2many:card_flights;joinForeignKey:FlightID;joinReferences:CardID"` + UserID uint64 `gorm:"-"` + CardID uint64 `gorm:"-"` } func (Flight) TableName() string { return "flights" } |
