aboutsummaryrefslogtreecommitdiff
path: root/pkg/model
diff options
context:
space:
mode:
authorleshe4ka46 <alex9102naid1@ya.ru>2025-10-27 20:36:28 +0300
committerleshe4ka46 <alex9102naid1@ya.ru>2025-10-28 13:42:21 +0300
commitbb833561aa74f02970aee13cdc75973b29716491 (patch)
tree0914668e11dbf825979f7419ce1bc78294cd3f7f /pkg/model
parente17a425dfb3382310fb5863f516dacdca9f44956 (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.go56
-rw-r--r--pkg/model/store.go7
-rw-r--r--pkg/model/types.go74
-rw-r--r--pkg/model/user.go66
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" }