-
Notifications
You must be signed in to change notification settings - Fork 0
/
filter.go
161 lines (135 loc) · 4.8 KB
/
filter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package twigots
import (
"errors"
"fmt"
"time"
"github.com/adrg/strutil"
"github.com/adrg/strutil/metrics"
"github.com/samber/lo"
)
const defaultSimilarity = 0.85
// A filter to use on ticket listing(s). A ticket listing can either match the filter or not.
//
// A ticket listing must satisfy all specified filter fields to match, making this an AND filter.
type Filter struct {
// Name of event on ticket listing to match.
// Required.
Event string
// Similarity of event name on ticket listing to the event name specified in the filter.
// Specified as a float, between 0 and 1 (with 1 representing an exact match).
// Leave this unset or set to <=0 to use the default.
// Default is 0.85 which accounts for variances in event names.
// e.g. Taylor Swift and Taylor Swift: The Eras Tour should match.
EventSimilarity float64
// Regions on ticket listings to match.
// Leave this unset or empty to match any region.
// Defaults to unset.
Regions []Region
// Number of tickets in a listing to match.
// Leave this unset or set to <=0 to match any number of tickets.
// Defaults to unset.
NumTickets int
// Minimum discount (including fee) of tickets in a listing to match.
// Specified as a float, between 0 and 1 (with 1 representing 100% off).
// Leave this unset or set to <=0 to match any discount (including no discount).
// Defaults to unset.
MinDiscount float64
// Time a listing must be created before to be match
// Leave this unset to match any creation time.
// Defaults to unset.
CreatedBefore time.Time
// Time a listing must be created after to be match
// Leave this unset to match any creation time.
// Defaults to unset.
CreatedAfter time.Time
}
// Validate the filter.
// This is used internally to check a filter, but can also be used externally.
func (f Filter) Validate() error {
if f.Event == "" {
return errors.New("event name must be set")
}
if f.EventSimilarity > 1 {
return errors.New("similarity cannot be > 1")
}
for _, region := range f.Regions {
if !Regions.Contains(region) {
return fmt.Errorf("region '%s' is not valid", region)
}
}
if f.MinDiscount > 1 {
return errors.New("discount cannot be > 1")
}
return nil
}
// matchesAnyFilter returns whether a ticket listing matches any of the filters provided.
// filters are assumed to have been validated first.
func matchesAnyFilter(listing TicketListing, filters ...Filter) bool {
if len(filters) == 0 {
return true
}
for _, filter := range filters {
if matchesFilter(listing, filter) {
return true
}
}
return false
}
// matchesAnyFilter returns whether a ticket listing matches the filters provided.
// filter is assumed to have been validated first.
func matchesFilter(listing TicketListing, filter Filter) bool {
return matchesEventName(listing, filter.Event, filter.EventSimilarity) &&
matchesRegions(listing, filter.Regions) &&
matchesNumTickets(listing, filter.NumTickets) &&
matchesDiscount(listing, filter.MinDiscount) &&
matchesCreatedBefore(listing, filter.CreatedBefore) &&
matchesCreatedAfter(listing, filter.CreatedAfter)
}
// matchesEventName returns whether a ticket listing matches a desired event name
func matchesEventName(listing TicketListing, eventName string, similarity float64) bool {
ticketEventName := normaliseEventName(listing.Event.Name)
desiredEventName := normaliseEventName(eventName)
ticketSimilarity := strutil.Similarity(
ticketEventName, desiredEventName,
metrics.NewJaroWinkler(),
)
if similarity <= 0 {
return ticketSimilarity >= defaultSimilarity
}
return ticketSimilarity >= similarity
}
// matchesRegions determines whether a ticket listing matches any desired regions.
func matchesRegions(listing TicketListing, regions []Region) bool {
if len(regions) == 0 {
return true
}
return lo.Contains(regions, listing.Event.Venue.Location.Region)
}
// matchesNumTickets determines whether a ticket listing matches a desired number of tickets
func matchesNumTickets(listing TicketListing, numTickets int) bool {
if numTickets <= 0 {
return true
}
return listing.NumTickets == numTickets
}
// matchesDiscount determines whether a ticket listing matches a desired discount.
func matchesDiscount(listing TicketListing, discount float64) bool {
if discount <= 0 {
return true
}
return listing.Discount() >= discount
}
// matchesCreatedBefore determines whether a ticket listing matches a desired created before time.
func matchesCreatedBefore(listing TicketListing, createdBefore time.Time) bool {
if createdBefore.IsZero() {
return true
}
return listing.CreatedAt.Time.Before(createdBefore)
}
// matchesCreatedAfter determines whether a ticket listing matches a desired created after time.
func matchesCreatedAfter(listing TicketListing, createdAfter time.Time) bool {
if createdAfter.IsZero() {
return true
}
return listing.CreatedAt.Time.After(createdAfter)
}