Skip to content

Commit

Permalink
add inclusion/exclusion for airport and flightnumber
Browse files Browse the repository at this point in the history
  • Loading branch information
its-felix committed May 12, 2024
1 parent 9cf9583 commit 9545a87
Show file tree
Hide file tree
Showing 9 changed files with 412 additions and 74 deletions.
41 changes: 8 additions & 33 deletions go/api/search/connections.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,6 @@ type Connection struct {
Outgoing []Connection
}

type ConnectionOption interface {
Matches(f *common.Flight) bool
}

type WithIncludeAircraft []string

func (a WithIncludeAircraft) Matches(f *common.Flight) bool {
return slices.Contains(a, f.AircraftType)
}

type WithExcludeAircraft []string

func (a WithExcludeAircraft) Matches(f *common.Flight) bool {
return !slices.Contains(a, f.AircraftType)
}

type ConnectionsHandler struct {
fr *FlightRepo
}
Expand All @@ -47,7 +31,7 @@ func (ch *ConnectionsHandler) FindConnections(ctx context.Context, origins, dest
return nil, err
}

flightsByDeparture = groupByDeparture(flightsByDate)
flightsByDeparture = groupByDeparture(flightsByDate, options)
}

return collectCtx(ctx, findConnections(
Expand All @@ -61,12 +45,11 @@ func (ch *ConnectionsHandler) FindConnections(ctx context.Context, origins, dest
minLayover,
maxLayover,
maxDuration,
options,
true,
))
}

func findConnections(ctx context.Context, flightsByDeparture map[common.Departure][]*common.Flight, origins, destinations []string, minDeparture, maxDeparture time.Time, maxFlights int, minLayover, maxLayover, maxDuration time.Duration, options []ConnectionOption, initial bool) <-chan Connection {
func findConnections(ctx context.Context, flightsByDeparture map[common.Departure][]*common.Flight, origins, destinations []string, minDeparture, maxDeparture time.Time, maxFlights int, minLayover, maxLayover, maxDuration time.Duration, initial bool) <-chan Connection {
if maxFlights < 1 || maxDuration < 1 {
ch := make(chan Connection)
close(ch)
Expand Down Expand Up @@ -94,7 +77,7 @@ func findConnections(ctx context.Context, flightsByDeparture map[common.Departur
}

for _, f := range flightsByDeparture[d] {
if f.ServiceType != "J" || f.Duration() > maxDuration || f.DepartureTime.Compare(minDeparture) < 0 || f.DepartureTime.Compare(maxDeparture) > 0 || !allMatch(options, f) {
if f.ServiceType != "J" || f.Duration() > maxDuration || f.DepartureTime.Compare(minDeparture) < 0 || f.DepartureTime.Compare(maxDeparture) > 0 {
continue
}

Expand Down Expand Up @@ -131,7 +114,6 @@ func findConnections(ctx context.Context, flightsByDeparture map[common.Departur
minLayover,
maxLayover,
maxDuration-(f.Duration()+minLayover),
options,
false,
)

Expand Down Expand Up @@ -182,27 +164,20 @@ func allMatch(options []ConnectionOption, f *common.Flight) bool {
return true
}

func groupByDeparture(flightsByDate map[common.LocalDate][]*common.Flight) map[common.Departure][]*common.Flight {
func groupByDeparture(flightsByDate map[common.LocalDate][]*common.Flight, options []ConnectionOption) map[common.Departure][]*common.Flight {
result := make(map[common.Departure][]*common.Flight)
for _, flights := range flightsByDate {
for _, f := range flights {
d := f.Departure()
result[d] = append(result[d], f)
if allMatch(options, f) {
d := f.Departure()
result[d] = append(result[d], f)
}
}
}

return result
}

func collect[T any](ch <-chan T) []T {
var r []T
for v := range ch {
r = append(r, v)
}

return r
}

func collectCtx[T any](ctx context.Context, ch <-chan T) ([]T, error) {
var r []T
for {
Expand Down
95 changes: 95 additions & 0 deletions go/api/search/option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package search

import (
"github.com/explore-flights/monorepo/go/common"
"path"
"slices"
)

type ConnectionOption interface {
Matches(f *common.Flight) bool
}

type WithIncludeAircraft []string

func (a WithIncludeAircraft) Matches(f *common.Flight) bool {
return slices.Contains(a, f.AircraftType)
}

type WithExcludeAircraft []string

func (a WithExcludeAircraft) Matches(f *common.Flight) bool {
return !slices.Contains(a, f.AircraftType)
}

type WithIncludeAircraftGlob []string

func (a WithIncludeAircraftGlob) Matches(f *common.Flight) bool {
return slices.ContainsFunc(a, func(s string) bool {
return globMatch(f.AircraftType, s)
})
}

type WithExcludeAircraftGlob []string

func (a WithExcludeAircraftGlob) Matches(f *common.Flight) bool {
return !WithIncludeAircraftGlob(a).Matches(f)
}

type WithIncludeAirport []string

func (a WithIncludeAirport) Matches(f *common.Flight) bool {
return slices.Contains(a, f.DepartureAirport) && slices.Contains(a, f.ArrivalAirport)
}

type WithExcludeAirport []string

func (a WithExcludeAirport) Matches(f *common.Flight) bool {
return !slices.Contains(a, f.DepartureAirport) && !slices.Contains(a, f.ArrivalAirport)
}

type WithIncludeAirportGlob []string

func (a WithIncludeAirportGlob) Matches(f *common.Flight) bool {
return slices.ContainsFunc(a, func(s string) bool {
return globMatch(f.DepartureAirport, s) && globMatch(f.ArrivalAirport, s)
})
}

type WithExcludeAirportGlob []string

func (a WithExcludeAirportGlob) Matches(f *common.Flight) bool {
return !WithIncludeAirportGlob(a).Matches(f)
}

type WithIncludeFlightNumber []string

func (a WithIncludeFlightNumber) Matches(f *common.Flight) bool {
return slices.Contains(a, f.Number().String())
}

type WithExcludeFlightNumber []string

func (a WithExcludeFlightNumber) Matches(f *common.Flight) bool {
return !slices.Contains(a, f.Number().String())
}

type WithIncludeFlightNumberGlob []string

func (a WithIncludeFlightNumberGlob) Matches(f *common.Flight) bool {
v := f.Number().String()
return slices.ContainsFunc(a, func(s string) bool {
return globMatch(v, s)
})
}

type WithExcludeFlightNumberGlob []string

func (a WithExcludeFlightNumberGlob) Matches(f *common.Flight) bool {
return !WithIncludeFlightNumberGlob(a).Matches(f)
}

func globMatch(v, pattern string) bool {
match, err := path.Match(pattern, v)
return err == nil && match
}
90 changes: 78 additions & 12 deletions go/api/web/connections.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@ import (
"context"
"errors"
"github.com/explore-flights/monorepo/go/api/search"
"github.com/explore-flights/monorepo/go/common"
"github.com/labstack/echo/v4"
"net/http"
"strings"
"time"
)

type ConnectionsSearchRequest struct {
Origins []string `json:"origins"`
Destinations []string `json:"destinations"`
MinDeparture time.Time `json:"minDeparture"`
MaxDeparture time.Time `json:"maxDeparture"`
MaxFlights int `json:"maxFlights"`
MinLayoverMS int `json:"minLayoverMS"`
MaxLayoverMS int `json:"maxLayoverMS"`
MaxDurationMS int `json:"maxDurationMS"`
IncludeAircraft *[]string `json:"includeAircraft,omitempty"`
ExcludeAircraft *[]string `json:"excludeAircraft,omitempty"`
Origins []string `json:"origins"`
Destinations []string `json:"destinations"`
MinDeparture time.Time `json:"minDeparture"`
MaxDeparture time.Time `json:"maxDeparture"`
MaxFlights int `json:"maxFlights"`
MinLayoverMS int `json:"minLayoverMS"`
MaxLayoverMS int `json:"maxLayoverMS"`
MaxDurationMS int `json:"maxDurationMS"`
IncludeAirport *[]string `json:"includeAirport,omitempty"`
ExcludeAirport *[]string `json:"excludeAirport,omitempty"`
IncludeFlightNumber *[]string `json:"includeFlightNumber,omitempty"`
ExcludeFlightNumber *[]string `json:"excludeFlightNumber,omitempty"`
IncludeAircraft *[]string `json:"includeAircraft,omitempty"`
ExcludeAircraft *[]string `json:"excludeAircraft,omitempty"`
}

func NewConnectionsEndpoint(ch *search.ConnectionsHandler) echo.HandlerFunc {
Expand All @@ -43,15 +49,43 @@ func NewConnectionsEndpoint(ch *search.ConnectionsHandler) echo.HandlerFunc {
return echo.NewHTTPError(http.StatusBadRequest, "maxFlights must be <=4")
} else if req.MaxDeparture.Add(maxDuration).Sub(req.MinDeparture) > time.Hour*24*14 {
return echo.NewHTTPError(http.StatusBadRequest, "range must be <=14d")
} else if req.IncludeAirport != nil && len(*req.IncludeAirport) > 100 {
return echo.NewHTTPError(http.StatusBadRequest, "len(IncludeAirport) must be <= 100")
} else if req.ExcludeAirport != nil && len(*req.ExcludeAirport) > 100 {
return echo.NewHTTPError(http.StatusBadRequest, "len(ExcludeAirport) must be <= 100")
} else if req.IncludeFlightNumber != nil && len(*req.IncludeFlightNumber) > 100 {
return echo.NewHTTPError(http.StatusBadRequest, "len(IncludeFlightNumber) must be <= 100")
} else if req.ExcludeFlightNumber != nil && len(*req.ExcludeFlightNumber) > 100 {
return echo.NewHTTPError(http.StatusBadRequest, "len(ExcludeFlightNumber) must be <= 100")
} else if req.IncludeAircraft != nil && len(*req.IncludeAircraft) > 100 {
return echo.NewHTTPError(http.StatusBadRequest, "len(IncludeAircraft) must be <= 100")
} else if req.ExcludeAircraft != nil && len(*req.ExcludeAircraft) > 100 {
return echo.NewHTTPError(http.StatusBadRequest, "len(ExcludeAircraft) must be <= 100")
}

options := make([]search.ConnectionOption, 0)
if req.IncludeAirport != nil {
options = appendOptions[search.WithIncludeAirport, search.WithIncludeAirportGlob](options, *req.IncludeAirport)
}

if req.ExcludeAirport != nil {
options = appendOptions[search.WithExcludeAirport, search.WithExcludeAirportGlob](options, *req.ExcludeAirport)
}

if req.IncludeFlightNumber != nil {
options = appendOptions[search.WithIncludeFlightNumber, search.WithIncludeFlightNumberGlob](options, *req.IncludeFlightNumber)
}

if req.ExcludeFlightNumber != nil {
options = appendOptions[search.WithExcludeFlightNumber, search.WithExcludeFlightNumberGlob](options, *req.ExcludeFlightNumber)
}

if req.IncludeAircraft != nil {
options = append(options, search.WithIncludeAircraft(*req.IncludeAircraft))
options = appendOptions[search.WithIncludeAircraft, search.WithIncludeAircraftGlob](options, *req.IncludeAircraft)
}

if req.ExcludeAircraft != nil {
options = append(options, search.WithExcludeAircraft(*req.ExcludeAircraft))
options = appendOptions[search.WithExcludeAircraft, search.WithExcludeAircraftGlob](options, *req.ExcludeAircraft)
}

conns, err := ch.FindConnections(
Expand Down Expand Up @@ -86,3 +120,35 @@ func NewConnectionsEndpoint(ch *search.ConnectionsHandler) echo.HandlerFunc {
}
}
}

type restr interface {
~[]string
Matches(f *common.Flight) bool
}

func appendOptions[Reg restr, Glob restr](options []search.ConnectionOption, values []string) []search.ConnectionOption {
regular := make(Reg, 0)
glob := make(Glob, 0)

for _, v := range values {
if hasMeta(v) {
glob = append(glob, v)
} else {
regular = append(regular, v)
}
}

if len(regular) > 0 {
options = append(options, regular)
}

if len(glob) > 0 {
options = append(options, glob)
}

return options
}

func hasMeta(pattern string) bool {
return strings.ContainsAny(pattern, "*?[\\")
}
Loading

0 comments on commit 9545a87

Please sign in to comment.