Skip to content

Commit

Permalink
add option to count multi-leg flights as 1
Browse files Browse the repository at this point in the history
  • Loading branch information
its-felix committed Oct 26, 2024
1 parent 5971a66 commit f4875e6
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 50 deletions.
55 changes: 34 additions & 21 deletions go/api/pb/connection_search_request.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 21 additions & 8 deletions go/api/search/connections.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (

type flightPredicate func(f *common.Flight) bool

type Filters struct {
all []flightPredicate
any []flightPredicate
type Options struct {
countMultiLeg bool
all []flightPredicate
any []flightPredicate
}

type Connection struct {
Expand All @@ -28,8 +29,8 @@ func NewConnectionsHandler(fr *FlightRepo) *ConnectionsHandler {
return &ConnectionsHandler{fr}
}

func (ch *ConnectionsHandler) FindConnections(ctx context.Context, origins, destinations []string, minDeparture, maxDeparture time.Time, maxFlights uint32, minLayover, maxLayover, maxDuration time.Duration, options ...FilterOption) ([]Connection, error) {
var f Filters
func (ch *ConnectionsHandler) FindConnections(ctx context.Context, origins, destinations []string, minDeparture, maxDeparture time.Time, maxFlights uint32, minLayover, maxLayover, maxDuration time.Duration, options ...ConnectionSearchOption) ([]Connection, error) {
var f Options
for _, opt := range options {
opt.Apply(&f)
}
Expand Down Expand Up @@ -59,6 +60,7 @@ func (ch *ConnectionsHandler) FindConnections(ctx context.Context, origins, dest
maxLayover,
maxDuration,
f.any,
f.countMultiLeg,
nil,
))
}
Expand All @@ -75,10 +77,11 @@ func findConnections(
maxLayover,
maxDuration time.Duration,
predicates []flightPredicate,
countMultiLeg bool,
incomingFn *common.FlightNumber,
) <-chan Connection {

if maxFlights < 1 || maxDuration < 1 {
if (countMultiLeg && maxFlights < 1) || maxDuration < 1 {
ch := make(chan Connection)
close(ch)
return ch
Expand Down Expand Up @@ -107,6 +110,7 @@ func findConnections(
for _, f := range flightsByDeparture[d] {
minDeparture := minDeparture
maxDuration := maxDuration
sameFlightNumber := false

if incomingFn != nil {
maxDuration = maxDuration - f.DepartureTime.Sub(minDeparture)
Expand All @@ -115,12 +119,14 @@ func findConnections(
if *incomingFn != f.Number() {
minDeparture = minDeparture.Add(minLayover)
maxDuration = maxDuration - minLayover
} else {
sameFlightNumber = true
}
}

// J = regular flight
// U = Rail&Fly
if (f.ServiceType != "J" && f.ServiceType != "U") || f.Duration() > maxDuration || f.DepartureTime.Compare(minDeparture) < 0 || f.DepartureTime.Compare(maxDeparture) > 0 {
if (f.ServiceType != "J" && f.ServiceType != "U") || (maxFlights < 1 && !sameFlightNumber) || f.Duration() > maxDuration || f.DepartureTime.Compare(minDeparture) < 0 || f.DepartureTime.Compare(maxDeparture) > 0 {
continue
}

Expand Down Expand Up @@ -148,18 +154,25 @@ func findConnections(
}
} else {
fn := f.Number()
consumeFlights := uint32(1)

if !countMultiLeg && sameFlightNumber {
consumeFlights = 0
}

subConns := findConnections(
ctx,
flightsByDeparture,
[]string{f.ArrivalAirport},
destinations,
f.ArrivalTime,
f.ArrivalTime.Add(maxLayover),
maxFlights-1,
maxFlights-consumeFlights,
minLayover,
maxLayover,
maxDuration-f.Duration(),
remPredicates,
countMultiLeg,
&fn,
)

Expand Down
34 changes: 20 additions & 14 deletions go/api/search/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,43 @@ import (
"slices"
)

type FilterOption interface {
Apply(f *Filters)
type ConnectionSearchOption interface {
Apply(f *Options)
}

type WithCountMultiLeg bool

func (a WithCountMultiLeg) Apply(f *Options) {
f.countMultiLeg = bool(a)
}

type WithIncludeAircraft string

func (a WithIncludeAircraft) Apply(f *Filters) {
func (a WithIncludeAircraft) Apply(f *Options) {
f.any = append(f.any, func(f *common.Flight) bool {
return f.AircraftType == string(a)
})
}

type WithExcludeAircraft []string

func (a WithExcludeAircraft) Apply(f *Filters) {
func (a WithExcludeAircraft) Apply(f *Options) {
f.all = append(f.all, func(f *common.Flight) bool {
return !slices.Contains(a, f.AircraftType)
})
}

type WithIncludeAircraftGlob string

func (a WithIncludeAircraftGlob) Apply(f *Filters) {
func (a WithIncludeAircraftGlob) Apply(f *Options) {
f.any = append(f.any, func(f *common.Flight) bool {
return globMatch(f.AircraftType, string(a))
})
}

type WithExcludeAircraftGlob []string

func (a WithExcludeAircraftGlob) Apply(f *Filters) {
func (a WithExcludeAircraftGlob) Apply(f *Options) {
f.all = append(f.all, func(f *common.Flight) bool {
return !slices.ContainsFunc(a, func(s string) bool {
return globMatch(f.AircraftType, s)
Expand All @@ -46,31 +52,31 @@ func (a WithExcludeAircraftGlob) Apply(f *Filters) {

type WithIncludeAirport string

func (a WithIncludeAirport) Apply(f *Filters) {
func (a WithIncludeAirport) Apply(f *Options) {
f.any = append(f.any, func(f *common.Flight) bool {
return f.DepartureAirport == string(a) || f.ArrivalAirport == string(a)
})
}

type WithExcludeAirport []string

func (a WithExcludeAirport) Apply(f *Filters) {
func (a WithExcludeAirport) Apply(f *Options) {
f.all = append(f.all, func(f *common.Flight) bool {
return !slices.Contains(a, f.DepartureAirport) && !slices.Contains(a, f.ArrivalAirport)
})
}

type WithIncludeAirportGlob string

func (a WithIncludeAirportGlob) Apply(f *Filters) {
func (a WithIncludeAirportGlob) Apply(f *Options) {
f.any = append(f.any, func(f *common.Flight) bool {
return globMatch(f.DepartureAirport, string(a)) || globMatch(f.ArrivalAirport, string(a))
})
}

type WithExcludeAirportGlob []string

func (a WithExcludeAirportGlob) Apply(f *Filters) {
func (a WithExcludeAirportGlob) Apply(f *Options) {
f.all = append(f.all, func(f *common.Flight) bool {
return !slices.ContainsFunc(a, func(s string) bool {
return globMatch(f.DepartureAirport, s) && globMatch(f.ArrivalAirport, s)
Expand All @@ -80,31 +86,31 @@ func (a WithExcludeAirportGlob) Apply(f *Filters) {

type WithIncludeFlightNumber string

func (a WithIncludeFlightNumber) Apply(f *Filters) {
func (a WithIncludeFlightNumber) Apply(f *Options) {
f.any = append(f.any, func(f *common.Flight) bool {
return f.Number().String() == string(a)
})
}

type WithExcludeFlightNumber []string

func (a WithExcludeFlightNumber) Apply(f *Filters) {
func (a WithExcludeFlightNumber) Apply(f *Options) {
f.all = append(f.all, func(f *common.Flight) bool {
return !slices.Contains(a, f.Number().String())
})
}

type WithIncludeFlightNumberGlob string

func (a WithIncludeFlightNumberGlob) Apply(f *Filters) {
func (a WithIncludeFlightNumberGlob) Apply(f *Options) {
f.any = append(f.any, func(f *common.Flight) bool {
return globMatch(f.Number().String(), string(a))
})
}

type WithExcludeFlightNumberGlob []string

func (a WithExcludeFlightNumberGlob) Apply(f *Filters) {
func (a WithExcludeFlightNumberGlob) Apply(f *Options) {
f.all = append(f.all, func(f *common.Flight) bool {
v := f.Number().String()
return !slices.ContainsFunc(a, func(s string) bool {
Expand Down
20 changes: 15 additions & 5 deletions go/api/web/connections.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type connectionsSearchRequest struct {
MinLayoverMS uint64 `json:"minLayoverMS"`
MaxLayoverMS uint64 `json:"maxLayoverMS"`
MaxDurationMS uint64 `json:"maxDurationMS"`
CountMultiLeg bool `json:"countMultiLeg"`
IncludeAirport []string `json:"includeAirport,omitempty"`
ExcludeAirport []string `json:"excludeAirport,omitempty"`
IncludeFlightNumber []string `json:"includeFlightNumber,omitempty"`
Expand All @@ -47,6 +48,7 @@ type connectionsSearchResponse struct {
}

func (req connectionsSearchRequest) toPb() proto.Message {
countMultiLeg := req.CountMultiLeg
return &pb.ConnectionsSearchRequest{
Origins: req.Origins,
Destinations: req.Destinations,
Expand All @@ -56,6 +58,7 @@ func (req connectionsSearchRequest) toPb() proto.Message {
MinLayover: durationpb.New(time.Duration(req.MinLayoverMS) * time.Millisecond),
MaxLayover: durationpb.New(time.Duration(req.MaxLayoverMS) * time.Millisecond),
MaxDuration: durationpb.New(time.Duration(req.MaxDurationMS) * time.Millisecond),
CountMultiLeg: &countMultiLeg,
IncludeAirport: req.IncludeAirport,
ExcludeAirport: req.ExcludeAirport,
IncludeFlightNumber: req.IncludeFlightNumber,
Expand All @@ -78,7 +81,8 @@ func NewConnectionsEndpoint(ch *search.ConnectionsHandler, export string) echo.H
maxLayover := time.Duration(req.MaxLayoverMS) * time.Millisecond
maxDuration := time.Duration(req.MaxDurationMS) * time.Millisecond

options := make([]search.FilterOption, 0)
options := make([]search.ConnectionSearchOption, 0)
options = append(options, search.WithCountMultiLeg(req.CountMultiLeg))
options = appendStringOptions[search.WithIncludeAirport, search.WithIncludeAirportGlob](options, req.IncludeAirport)
options = appendSliceOptions[search.WithExcludeAirport, search.WithExcludeAirportGlob](options, req.ExcludeAirport)
options = appendStringOptions[search.WithIncludeFlightNumber, search.WithIncludeFlightNumberGlob](options, req.IncludeFlightNumber)
Expand Down Expand Up @@ -238,6 +242,11 @@ func parseRequest(c echo.Context) (connectionsSearchRequest, error) {
return connectionsSearchRequest{}, err
}

countMultiLeg := true // multi-leg flights were counted before this option was added
if pbReq.CountMultiLeg != nil {
countMultiLeg = *pbReq.CountMultiLeg
}

req = connectionsSearchRequest{
Origins: pbReq.Origins,
Destinations: pbReq.Destinations,
Expand All @@ -247,6 +256,7 @@ func parseRequest(c echo.Context) (connectionsSearchRequest, error) {
MinLayoverMS: uint64(pbReq.MinLayover.AsDuration().Milliseconds()),
MaxLayoverMS: uint64(pbReq.MaxLayover.AsDuration().Milliseconds()),
MaxDurationMS: uint64(pbReq.MaxDuration.AsDuration().Milliseconds()),
CountMultiLeg: countMultiLeg,
IncludeAirport: pbReq.IncludeAirport,
ExcludeAirport: pbReq.ExcludeAirport,
IncludeFlightNumber: pbReq.IncludeFlightNumber,
Expand Down Expand Up @@ -289,10 +299,10 @@ func validateRequest(req connectionsSearchRequest) error {

type sliceRestr interface {
~[]string
search.FilterOption
search.ConnectionSearchOption
}

func appendSliceOptions[Reg sliceRestr, Glob sliceRestr](options []search.FilterOption, values []string) []search.FilterOption {
func appendSliceOptions[Reg sliceRestr, Glob sliceRestr](options []search.ConnectionSearchOption, values []string) []search.ConnectionSearchOption {
unique := make(map[string]struct{})
regular := make(Reg, 0)
glob := make(Glob, 0)
Expand Down Expand Up @@ -324,10 +334,10 @@ func appendSliceOptions[Reg sliceRestr, Glob sliceRestr](options []search.Filter

type stringRestr interface {
~string
search.FilterOption
search.ConnectionSearchOption
}

func appendStringOptions[Reg stringRestr, Glob stringRestr](options []search.FilterOption, values []string) []search.FilterOption {
func appendStringOptions[Reg stringRestr, Glob stringRestr](options []search.ConnectionSearchOption, values []string) []search.ConnectionSearchOption {
unique := make(map[string]struct{})

for _, v := range values {
Expand Down
Loading

0 comments on commit f4875e6

Please sign in to comment.