Skip to content

Commit

Permalink
Add name similarity to config
Browse files Browse the repository at this point in the history
  • Loading branch information
ahobsonsayers committed Sep 29, 2024
1 parent 2e2fda6 commit 74fea62
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 48 deletions.
36 changes: 23 additions & 13 deletions cmd/twitchets/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ func (c Config) Filters() []twickets.Filter {
var filter twickets.Filter
filter.Name = ticketConfig.Name

// Set name similarity
if ticketConfig.NameSimilarity == nil {
filter.NameSimilarity = c.GlobalConfig.NameSimilarity
} else if *ticketConfig.NameSimilarity > 0 {
filter.NameSimilarity = *ticketConfig.NameSimilarity
}

// Set regions
if ticketConfig.Regions == nil {
filter.Regions = c.GlobalConfig.Regions
Expand Down Expand Up @@ -83,20 +90,22 @@ func (c Config) Filters() []twickets.Filter {
// unless an event explicitly overwrites its.
// Country is required.
type GlobalEventConfig struct {
Regions []twickets.Region `json:"regions"`
NumTickets int `json:"numTickets"`
Discount float64 `json:"discount"`
NameSimilarity float64 `json:"nameSimilarity"`
Regions []twickets.Region `json:"regions"`
NumTickets int `json:"numTickets"`
Discount float64 `json:"discount"`
}

func (c GlobalEventConfig) Validate() error {
// Reuse the filter validation logic
globalFilter := twickets.Filter{
Name: "global", // Name must be be set - this is arbitrary
Regions: c.Regions,
NumTickets: c.NumTickets,
Discount: c.Discount,
filter := twickets.Filter{
Name: "global", // Name must be be set - this is arbitrary
NameSimilarity: c.NameSimilarity,
Regions: c.Regions,
NumTickets: c.NumTickets,
Discount: c.Discount,
}
err := globalFilter.Validate()
err := filter.Validate()
if err != nil {
return err
}
Expand All @@ -105,10 +114,11 @@ func (c GlobalEventConfig) Validate() error {
}

type TicketConfig struct {
Name string `json:"name"`
Regions []twickets.Region `json:"regions"`
NumTickets *int `json:"numTickets"`
Discount *float64 `json:"discount"`
Name string `json:"name"`
NameSimilarity *float64 `json:"nameSimilarity"`
Regions []twickets.Region `json:"regions"`
NumTickets *int `json:"numTickets"`
Discount *float64 `json:"discount"`
}

func (t TicketConfig) Validate() error {
Expand Down
77 changes: 50 additions & 27 deletions cmd/twitchets/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ func TestLoadConfig(t *testing.T) {
require.NoError(t, err)

country := twickets.CountryUnitedKingdom

globalNameSimilarity := 75.0
globalRegions := []twickets.Region{twickets.RegionLondon, twickets.RegionNorthWest}
globalNumTickets := 2
globalDiscount := 25.0
Expand All @@ -23,9 +25,10 @@ func TestLoadConfig(t *testing.T) {
APIKey: "test",
Country: country,
GlobalConfig: GlobalEventConfig{
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: globalDiscount,
NameSimilarity: globalNameSimilarity,
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: globalDiscount,
},
TicketsConfig: []TicketConfig{
{
Expand All @@ -47,12 +50,18 @@ func TestLoadConfig(t *testing.T) {
Name: "Event 4",
Discount: lo.ToPtr(15.0),
},
{
// Event with name similarity set
Name: "Event 5",
NameSimilarity: lo.ToPtr(90.0),
},
{
// Event with globals unset
Name: "Event 5",
Regions: []twickets.Region{},
NumTickets: lo.ToPtr(-1),
Discount: lo.ToPtr(-1.0),
Name: "Event 6",
NameSimilarity: lo.ToPtr(-1.0),
Regions: []twickets.Region{},
NumTickets: lo.ToPtr(-1),
Discount: lo.ToPtr(-1.0),
},
},
}
Expand All @@ -67,45 +76,59 @@ func TestConfigFilters(t *testing.T) {

actualFilters := config.Filters()

globalNameSimilarity := 75.0
globalRegions := []twickets.Region{twickets.RegionLondon, twickets.RegionNorthWest}
globalNumTickets := 2
globalDiscount := 25.0

expectedFilters := []twickets.Filter{
{
// Event with only name set
Name: "Event 1",
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: globalDiscount,
Name: "Event 1",
NameSimilarity: globalNameSimilarity,
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: globalDiscount,
},
{
// Event with regions set
Name: "Event 2",
Regions: []twickets.Region{twickets.RegionSouthWest},
NumTickets: globalNumTickets,
Discount: globalDiscount,
Name: "Event 2",
NameSimilarity: globalNameSimilarity,
Regions: []twickets.Region{twickets.RegionSouthWest},
NumTickets: globalNumTickets,
Discount: globalDiscount,
},
{
// Event with num tickets set
Name: "Event 3",
Regions: globalRegions,
NumTickets: 1,
Discount: globalDiscount,
Name: "Event 3",
NameSimilarity: globalNameSimilarity,
Regions: globalRegions,
NumTickets: 1,
Discount: globalDiscount,
},
{
// Event with discount set
Name: "Event 4",
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: 15.0,
Name: "Event 4",
NameSimilarity: globalNameSimilarity,
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: 15.0,
},
{
// Event with name similarity set
Name: "Event 5",
NameSimilarity: 90.0,
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: globalDiscount,
},
{
// Event with globals unset
Name: "Event 5",
Regions: []twickets.Region{},
NumTickets: 0,
Discount: 0.0,
Name: "Event 6",
NameSimilarity: 0.0,
Regions: []twickets.Region{},
NumTickets: 0,
Discount: 0.0,
},
}

Expand Down
4 changes: 4 additions & 0 deletions test/assets/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ global:
- GBNW
numTickets: 2
discount: 25
nameSimilarity: 75

tickets:
- name: Event 1
Expand All @@ -17,6 +18,9 @@ tickets:
- name: Event 4
discount: 15
- name: Event 5
nameSimilarity: 90
- name: Event 6
nameSimilarity: -1
regions: []
numTickets: -1
discount: -1
29 changes: 21 additions & 8 deletions twickets/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,24 @@ import (
)

type Filter struct {
Name string
Regions []Region
NumTickets int
Discount float64
Name string
NameSimilarity float64
Regions []Region
NumTickets int
Discount float64
}

func (f Filter) Validate() error {
if f.Name == "" {
return errors.New("event name must be set")
}

if f.NameSimilarity < 0 {
return errors.New("similarity cannot be negative")
} else if f.NameSimilarity > 100 {
return errors.New("similarity cannot above 100%")
}

for _, region := range f.Regions {
if !Regions.Contains(region) {
return fmt.Errorf("region '%s' is not valid", region)
Expand All @@ -41,19 +48,25 @@ func (f Filter) Validate() error {

// TicketMatches check is a ticket matches the filter
func (f Filter) TicketMatches(ticket Ticket) bool {
return matchesEventName(ticket, f.Name) &&
return matchesEventName(ticket, f.Name, f.NameSimilarity) &&
matchesRegions(ticket, f.Regions) &&
matchesNumTickets(ticket, f.NumTickets) &&
matchesDiscount(ticket, f.Discount)
}

// matchesEventName returns whether a tickets matches a desired event name
func matchesEventName(ticket Ticket, eventName string) bool {
func matchesEventName(ticket Ticket, eventName string, similarity float64) bool {
ticketEventName := normaliseEventName(ticket.Event.Name)
desiredEventName := normaliseEventName(eventName)

similarity := strutil.Similarity(ticketEventName, desiredEventName, metrics.NewJaroWinkler())
return similarity >= 0.85
ticketSimilarity := strutil.Similarity(
ticketEventName, desiredEventName,
metrics.NewJaroWinkler(),
)
if similarity == 0 {
return ticketSimilarity >= 0.85
}
return ticketSimilarity >= similarity/100
}

// matchesRegions determines whether a tickets matches any of desired regions
Expand Down

0 comments on commit 74fea62

Please sign in to comment.