refactor: Update parsing of flight schedule data for improved accuracy and functionality
This commit is contained in:
@@ -23,7 +23,7 @@ require (
|
|||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.2 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7 // indirect
|
github.com/lithammer/shortuuid/v3 v3.0.7 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
@@ -32,21 +32,21 @@ require (
|
|||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/vektah/gqlparser/v2 v2.5.11 // indirect
|
github.com/vektah/gqlparser/v2 v2.5.16 // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.25.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.27.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/text v0.16.0 // indirect
|
||||||
golang.org/x/tools v0.21.0 // indirect
|
golang.org/x/tools v0.23.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
+23
-25
@@ -29,25 +29,27 @@ type FlightSchedule struct {
|
|||||||
// Example: "ILS(0)"
|
// Example: "ILS(0)"
|
||||||
ILS string `json:"ils,omitempty"`
|
ILS string `json:"ils,omitempty"`
|
||||||
|
|
||||||
// ICAO code for the departure airport.
|
// // ICAO code for the departure airport.
|
||||||
// Example: "ZBTJ"
|
// // Example: "ZBTJ"
|
||||||
DepartureAirport string `json:"departure_airport"`
|
// DepartureAirport string `json:"departure_airport"`
|
||||||
|
|
||||||
// Scheduled departure time of the flight. This field may be formatted as HHMM or include date information.
|
// // Scheduled departure time of the flight. This field may be formatted as HHMM or include date information.
|
||||||
// Example: "1845"
|
// // Example: "1845"
|
||||||
DepartureTime string `json:"departure_time,omitempty"`
|
// DepartureTime string `json:"departure_time,omitempty"`
|
||||||
|
|
||||||
// Additional schedule information. This field may include special instructions or additional scheduling details.
|
// // Additional schedule information. This field may include special instructions or additional scheduling details.
|
||||||
// Example: "SI:TSN1845"
|
// // Example: "SI:TSN1845"
|
||||||
ScheduleInfo string `json:"schedule_info,omitempty"`
|
// ScheduleInfo string `json:"schedule_info,omitempty"`
|
||||||
|
|
||||||
// Estimated or scheduled arrival time of the flight. This field may be formatted as HHMM or include date information.
|
// // Estimated or scheduled arrival time of the flight. This field may be formatted as HHMM or include date information.
|
||||||
// Example: "2100"
|
// // Example: "2100"
|
||||||
ArrivalTime string `json:"arrival_time,omitempty"`
|
// ArrivalTime string `json:"arrival_time,omitempty"`
|
||||||
|
|
||||||
// ICAO code for the arrival airport.
|
// // ICAO code for the arrival airport.
|
||||||
// Example: "ZSPD"
|
// // Example: "ZSPD"
|
||||||
ArrivalAirport string `json:"arrival_airport"`
|
// ArrivalAirport string `json:"arrival_airport"`
|
||||||
|
|
||||||
|
Waypoints []WayPoint `json:"waypoints"`
|
||||||
|
|
||||||
// Additional comments or remarks about the flight schedule. This field may include any relevant notes or observations.
|
// Additional comments or remarks about the flight schedule. This field may include any relevant notes or observations.
|
||||||
// Example: "Special cargo handling required"
|
// Example: "Special cargo handling required"
|
||||||
@@ -57,6 +59,12 @@ type FlightSchedule struct {
|
|||||||
Reference string `json:"reference,omitempty"`
|
Reference string `json:"reference,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WayPoint struct {
|
||||||
|
ArrivalTime string
|
||||||
|
Airport string
|
||||||
|
DepartureTime string
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FlightSchedule) Validate() error {
|
func (f *FlightSchedule) Validate() error {
|
||||||
if f.Date == "" {
|
if f.Date == "" {
|
||||||
return fmt.Errorf("date is required")
|
return fmt.Errorf("date is required")
|
||||||
@@ -67,15 +75,5 @@ func (f *FlightSchedule) Validate() error {
|
|||||||
if f.AircraftReg == "" {
|
if f.AircraftReg == "" {
|
||||||
return fmt.Errorf("aircraft registration is required")
|
return fmt.Errorf("aircraft registration is required")
|
||||||
}
|
}
|
||||||
if f.DepartureAirport == "" {
|
|
||||||
return fmt.Errorf("departure airport is required")
|
|
||||||
}
|
|
||||||
if f.ArrivalAirport == "" {
|
|
||||||
return fmt.Errorf("arrival airport is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.DepartureTime == "" && f.ArrivalTime == "" {
|
|
||||||
return fmt.Errorf("either departure time or arrival time is required")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,19 @@ package parsers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"caatsm/internal/domain"
|
"caatsm/internal/domain"
|
||||||
|
"caatsm/pkg/utils"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DepartureCode = "dep"
|
|
||||||
// DepartureTime = "dep_time"
|
|
||||||
// ArrivalCode = "arr"
|
|
||||||
AirportCode = "airport"
|
AirportCode = "airport"
|
||||||
Date = "date"
|
Date = "date"
|
||||||
Task = "task"
|
Task = "task"
|
||||||
IndexPattern = `(?P<idx>\(?L?[0-9]+\)?\.?)`
|
IndexPattern = `(?P<idx>\(?L?[0-9]+\)?\.?)`
|
||||||
DatePattern = `\s?(?P<date>\d{2}\w{3})`
|
DatePattern = `\s?(?P<date>\d{2}\w{3})`
|
||||||
TaskPattern = `\s?(?P<task>[A-Z]\/[A-Z])`
|
TaskPattern = `\s?(?P<task>[A-Z]\/[A-Z])`
|
||||||
WaypointPattern = `\s?(?P<arr_time>\d{4}(\(\d{2}\w{3}\))?)\/?(?P<airport>\w{3})\/?(?P<dep_time>\d{4}(\(\d{2}\w{3}\))?)`
|
WaypointPattern = `^(SI:)?(?P<arr_time>\d{4}(\(\d{2}[A-Z]{3}\))?)?\/?(?P<airport>[A-Z]{3})\/?(?P<dep_time>\d{4}(\(\d{2}[A-Z]{3}\))?)?$`
|
||||||
FlightNumberPattern = `\s?(?P<number>[0-9A-Z][0-9A-Z]\d{3,5})`
|
FlightNumberPattern = `\s?(?P<number>[0-9A-Z][0-9A-Z]\d{3,5})`
|
||||||
RegisterPattern = `\s?(?P<reg>B\d{4})`
|
RegisterPattern = `\s?(?P<reg>B\d{4})`
|
||||||
)
|
)
|
||||||
@@ -38,31 +36,43 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func FindWaypoints(message string) map[string]string {
|
func ExtractWaypoint(message string) *domain.WayPoint {
|
||||||
matches := WaypointExpression.FindStringSubmatch(message)
|
matches := WaypointExpression.FindStringSubmatch(message)
|
||||||
if matches == nil {
|
if matches == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make(map[string]string)
|
data := make(map[string]string)
|
||||||
for i, name := range WaypointExpression.SubexpNames() {
|
for i, name := range WaypointExpression.SubexpNames() {
|
||||||
if i != 0 && name != "" {
|
if i != 0 && name != "" {
|
||||||
result[name] = matches[i]
|
data[name] = matches[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result := &domain.WayPoint{
|
||||||
|
ArrivalTime: data[ArrivalTime],
|
||||||
|
Airport: data[AirportCode],
|
||||||
|
DepartureTime: data[DepartureTime],
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseLine(line string) *domain.FlightSchedule {
|
func ParseLine(line string) *domain.FlightSchedule {
|
||||||
|
log := utils.GetSugaredLogger()
|
||||||
cleanLine := strings.TrimSpace(line)
|
cleanLine := strings.TrimSpace(line)
|
||||||
words := strings.Split(cleanLine, " ")
|
words := strings.Split(cleanLine, " ")
|
||||||
var flightSchedule = &domain.FlightSchedule{}
|
var flightSchedule = &domain.FlightSchedule{
|
||||||
|
Reference: line,
|
||||||
|
}
|
||||||
var data map[string]string
|
var data map[string]string
|
||||||
|
|
||||||
|
if indexData := parse(words[0], IndexExpression); indexData != nil {
|
||||||
|
flightSchedule.Index = indexData[Index]
|
||||||
|
words = words[1:]
|
||||||
|
}
|
||||||
|
|
||||||
// Define the parsing strategy
|
// Define the parsing strategy
|
||||||
parseStrategy := []string{
|
parseStrategy := []string{
|
||||||
Index,
|
|
||||||
Task,
|
Task,
|
||||||
Date,
|
Date,
|
||||||
FlightNumber,
|
FlightNumber,
|
||||||
@@ -71,38 +81,81 @@ func ParseLine(line string) *domain.FlightSchedule {
|
|||||||
|
|
||||||
// Track parsed fields to avoid re-parsing
|
// Track parsed fields to avoid re-parsing
|
||||||
parsed := make(map[string]bool)
|
parsed := make(map[string]bool)
|
||||||
|
var maxParsed int
|
||||||
|
|
||||||
|
// Parse each word in the line
|
||||||
for i, word := range words {
|
for i, word := range words {
|
||||||
if i > 0 {
|
// Check if all fields have been parsed
|
||||||
parsed[Index] = true
|
|
||||||
}
|
|
||||||
for _, name := range parseStrategy {
|
for _, name := range parseStrategy {
|
||||||
|
// Skip if already parsed
|
||||||
if parsed[name] {
|
if parsed[name] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// Parse the word
|
||||||
if data = parse(word, parserMap[name]); data != nil {
|
if data = parse(word, parserMap[name]); data != nil {
|
||||||
|
// Update the flight schedule
|
||||||
switch name {
|
switch name {
|
||||||
case Index:
|
|
||||||
flightSchedule.Index = data[Index]
|
|
||||||
parsed[Index] = true
|
|
||||||
case Task:
|
case Task:
|
||||||
flightSchedule.Task = data[Task]
|
flightSchedule.Task = data[Task]
|
||||||
parsed[Task] = true
|
parsed[Task] = true
|
||||||
|
maxParsed = i
|
||||||
case Date:
|
case Date:
|
||||||
flightSchedule.Date = data[Date]
|
flightSchedule.Date = data[Date]
|
||||||
parsed[Date] = true
|
parsed[Date] = true
|
||||||
|
maxParsed = i
|
||||||
case FlightNumber:
|
case FlightNumber:
|
||||||
flightSchedule.FlightNumber = data[FlightNumber]
|
flightSchedule.FlightNumber = data[FlightNumber]
|
||||||
parsed[FlightNumber] = true
|
parsed[FlightNumber] = true
|
||||||
|
maxParsed = i
|
||||||
case Register:
|
case Register:
|
||||||
flightSchedule.AircraftReg = data[Register]
|
flightSchedule.AircraftReg = data[Register]
|
||||||
parsed[Register] = true
|
parsed[Register] = true
|
||||||
|
maxParsed = i
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if maxParsed+1 < len(words) {
|
||||||
|
flightSchedule.Waypoints = parseWaypoints(words[maxParsed+1:])
|
||||||
|
} else {
|
||||||
|
log.Warn("No waypoints found")
|
||||||
|
flightSchedule.Comments = "No waypoints found"
|
||||||
}
|
}
|
||||||
|
|
||||||
return flightSchedule
|
return flightSchedule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseWaypoints(points []string) []domain.WayPoint {
|
||||||
|
log := utils.GetSugaredLogger()
|
||||||
|
if len(points) == 0 {
|
||||||
|
log.Warn("No waypoints found")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//find first waypoint
|
||||||
|
var realWaypoints []string
|
||||||
|
for i, point := range points {
|
||||||
|
if parse(point, WaypointExpression) != nil {
|
||||||
|
realWaypoints = points[i:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(realWaypoints) == 0 {
|
||||||
|
log.Warn("No waypoints found")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var waypoints []domain.WayPoint
|
||||||
|
for _, point := range realWaypoints {
|
||||||
|
log.Debugf("Parsing waypoint: %s", point)
|
||||||
|
if waypoint := ExtractWaypoint(point); waypoint != nil {
|
||||||
|
log.Debugf("Waypoint: %v", waypoint)
|
||||||
|
waypoints = append(waypoints, *waypoint)
|
||||||
|
} else {
|
||||||
|
log.Warnf("Failed to parse waypoint: %s", point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf("Found %d waypoints", len(waypoints))
|
||||||
|
return waypoints
|
||||||
|
}
|
||||||
|
|||||||
@@ -105,16 +105,16 @@ var _ = Describe("Schedule Parser", func() {
|
|||||||
Describe("FindWaypoints", func() {
|
Describe("FindWaypoints", func() {
|
||||||
It("should return the correct waypoints based on the message", func() {
|
It("should return the correct waypoints based on the message", func() {
|
||||||
message := "1845(11JUN)TSN/2100"
|
message := "1845(11JUN)TSN/2100"
|
||||||
waypoints := FindWaypoints(message)
|
waypoint := ExtractWaypoint(message)
|
||||||
Expect(waypoints).NotTo(BeNil())
|
Expect(waypoint).NotTo(BeNil())
|
||||||
Expect(waypoints[ArrivalTime]).To(Equal("1845(11JUN)"))
|
Expect(waypoint.ArrivalTime).To(Equal("1845(11JUN)"))
|
||||||
Expect(waypoints[AirportCode]).To(Equal("TSN"))
|
Expect(waypoint.Airport).To(Equal("TSN"))
|
||||||
Expect(waypoints[DepartureTime]).To(Equal("2100"))
|
Expect(waypoint.DepartureTime).To(Equal("2100"))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should return nil if no waypoints are found", func() {
|
It("should return nil if no waypoints are found", func() {
|
||||||
message := "1845TSN"
|
message := "18451TSN"
|
||||||
waypoints := FindWaypoints(message)
|
waypoints := ExtractWaypoint(message)
|
||||||
Expect(waypoints).To(BeNil())
|
Expect(waypoints).To(BeNil())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -130,6 +130,10 @@ var _ = Describe("Schedule Parser", func() {
|
|||||||
// Expect(schedule.Task).To(Equal("1/1"))
|
// Expect(schedule.Task).To(Equal("1/1"))
|
||||||
Expect(schedule.FlightNumber).To(Equal("FM9134"))
|
Expect(schedule.FlightNumber).To(Equal("FM9134"))
|
||||||
Expect(schedule.AircraftReg).To(Equal("B2688"))
|
Expect(schedule.AircraftReg).To(Equal("B2688"))
|
||||||
|
Expect(len(schedule.Waypoints)).To(Equal(2))
|
||||||
|
Expect(schedule.Waypoints[0].Airport).To(Equal("TSN"))
|
||||||
|
Expect(schedule.Waypoints[0].DepartureTime).To(Equal("0100"))
|
||||||
|
Expect(schedule.Waypoints[1].Airport).To(Equal("SHA"))
|
||||||
// Expect(schedule.PassengerConfig).To(Equal("1/1"))
|
// Expect(schedule.PassengerConfig).To(Equal("1/1"))
|
||||||
// Expect(schedule.ILS).To(Equal("ILS (00)"))
|
// Expect(schedule.ILS).To(Equal("ILS (00)"))
|
||||||
// Expect(schedule.DepartureAirport).To(Equal("TSN"))
|
// Expect(schedule.DepartureAirport).To(Equal("TSN"))
|
||||||
|
|||||||
Reference in New Issue
Block a user