sportdb-search 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a25ed40d388f7a3ce09acc8a67a5a16aec359696141c3260bda097b5d221c130
4
- data.tar.gz: ec681b64b842e733d874ab9c264e65797356129a0beccf11d9d40d55d1ea34fa
3
+ metadata.gz: c135a538a3ebe251b9506a6a31e0dcad7b3a456ed863a1a56ae3235a00129fd5
4
+ data.tar.gz: 145edb5ec66df797b8361f82da6c493417354ab408e19fa1a74fbb6f39d87d1f
5
5
  SHA512:
6
- metadata.gz: 78d1cbcea722b03bbb6d6986fda9837b3d15e1aaf9dd8b5085d382435e56a67bf62c228f0b33ff4ff01845e5a78e4ea86163ba1a150e8e9166ac66e0d5a81867
7
- data.tar.gz: 7e17104eb185b9c4f82e12eff407e9df4c352c6884194ae04036dd4bae690d0671c10ff723387bb12da54212a6033a3490a1763dfdbb55fd91cb916025d7199d
6
+ metadata.gz: 8e6604db8b3fb94a5fd1aa6656bb074ea6486765c0018eab262af941ec294323a3dd496d3782c8a735ce36b25650ef56e39bf7b536c8f059159a82c3d54e4ad6
7
+ data.tar.gz: 4dddf10fd601263a12f4ea7845e61261e6983a8d9c8f7420bc927d2149081c1420bd7e2793ab1a8e50a8b3aab18a9d21f411c135caab09ce1c72d0a9035c8270
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.1.1
1
+ ### 0.2.0
2
2
 
3
3
  ### 0.0.1 / 2024-08-25
4
4
 
data/Manifest.txt CHANGED
@@ -2,8 +2,13 @@ CHANGELOG.md
2
2
  Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
+ bin/fbq
5
6
  lib/sportdb/search.rb
6
- lib/sportdb/search/sport.rb
7
- lib/sportdb/search/structs.rb
8
- lib/sportdb/search/structs_world.rb
7
+ lib/sportdb/search/sport-clubs.rb
8
+ lib/sportdb/search/sport-events.rb
9
+ lib/sportdb/search/sport-history.rb
10
+ lib/sportdb/search/sport-leagues.rb
11
+ lib/sportdb/search/sport-more.rb
12
+ lib/sportdb/search/sport-teams.rb
9
13
  lib/sportdb/search/version.rb
14
+ lib/sportdb/search/world.rb
data/bin/fbq ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## tip: to test run:
4
+ ## ruby -I ./lib bin/fbq
5
+
6
+ ## our own code
7
+ require 'sportdb/search'
8
+
9
+
10
+
11
+ require 'optparse'
12
+
13
+
14
+
15
+ ## local hack
16
+ ## if exists up-to-date catalog db (use local version NOT built-in)
17
+ catalog_path = '../catalog/catalog.db'
18
+ if File.exist?( catalog_path )
19
+ SportDb::Import.config.catalog_path = catalog_path
20
+ end
21
+
22
+
23
+ args = ARGV
24
+ opts = { debug: false,
25
+ }
26
+
27
+ parser = OptionParser.new do |parser|
28
+ parser.banner = "Usage: #{$PROGRAM_NAME} [options] QUERY"
29
+
30
+ ##
31
+ ## check if git has a offline option?? (use same)
32
+ ## check for other tools - why? why not?
33
+ # parser.on( "-q", "--quiet",
34
+ # "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
35
+ # opts[:debug] = false
36
+ # end
37
+ parser.on( "--verbose", "--debug",
38
+ "turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
39
+ opts[:debug] = true
40
+ end
41
+ end
42
+ parser.parse!( args )
43
+
44
+ puts "OPTS:"
45
+ p opts
46
+ puts "ARGV:"
47
+ p args
48
+
49
+
50
+ ## add convenience shortcuts
51
+ Club = Sports::Club
52
+ NationalTeam = Sports::NationalTeam
53
+ League = Sports::League
54
+ Ground = Sports::Ground
55
+
56
+ City = Sports::City
57
+ Country = Sports::Country
58
+
59
+
60
+ queries = args
61
+ queries.each_with_index do |query,i|
62
+ puts "==> [#{i+1}/#{queries.size}] >>> #{query} <<< ..."
63
+
64
+ ## check for clubs
65
+ m = Club.match_by( name: query )
66
+ if m.size > 0
67
+ puts " #{m.size} CLUB match(es):"
68
+ print " "
69
+ print m.pretty_inspect
70
+ end
71
+
72
+ ## check for leagues
73
+ m = League.match_by( code: query )
74
+ if m.size > 0
75
+ puts " #{m.size} LEAGUE (by code) match(es):"
76
+ print " "
77
+ print m.pretty_inspect
78
+ end
79
+ m = League.match_by( name: query )
80
+ if m.size > 0
81
+ puts " #{m.size} LEAGUE (by name) match(es):"
82
+ print " "
83
+ print m.pretty_inspect
84
+ end
85
+
86
+ ## check for grounds/stadiums
87
+ m = Ground.match_by( name: query )
88
+ if m.size > 0
89
+ puts " #{m.size} GROUND match(es):"
90
+ print " "
91
+ print m.pretty_inspect
92
+ end
93
+
94
+
95
+ ### check for national team
96
+ rec = NationalTeam.find( query )
97
+ if rec
98
+ puts " 1 NATIONAL_TEAM match / found:"
99
+ print " "
100
+ print rec.pretty_inspect
101
+ end
102
+
103
+ ####
104
+ # check geos
105
+ # note - country for now must be unique match (thus, find)
106
+ rec = Country.find_by( code: query )
107
+ if rec
108
+ puts " 1 COUNTRY (by code) match / found:"
109
+ print " "
110
+ print rec.pretty_inspect
111
+ end
112
+ rec = Country.find_by( name: query )
113
+ if rec
114
+ puts " 1 COUNTRY (by name) match / found:"
115
+ print " "
116
+ print rec.pretty_inspect
117
+ end
118
+
119
+ m = City.match_by( name: query )
120
+ if m.size > 0
121
+ puts " #{m.size} CITY match(es):"
122
+ print " "
123
+ print m.pretty_inspect
124
+ end
125
+ end
126
+
127
+
128
+ puts "bye"
@@ -0,0 +1,132 @@
1
+
2
+ module Sports
3
+
4
+
5
+ class Club
6
+ def self._search() CatalogDb::Metal::Club; end
7
+
8
+ ###################
9
+ ## core required delegates - use delegate generator - why? why not?
10
+
11
+ ## add mods here - why? why not?
12
+ def self.match_by( name:, country: nil,
13
+ league: nil,
14
+ mods: nil )
15
+ ## for now assume "global" mods - checks only for name
16
+ ##
17
+ if mods && mods[ name ]
18
+ club = mods[ name ]
19
+ return [club] # note: wrap (single record) in array
20
+ end
21
+
22
+ ## note: add "auto-magic" country calculation via league record
23
+ ## if league is a national league for football clubs
24
+ if league
25
+ raise ArgumentError, "match_by - league AND country NOT supported; sorry" if country
26
+ ### find countries via league
27
+ ### support league.intl? too - why? why not?
28
+ ### or only nationa league
29
+ raise ArgumentError, "match_by - league - only national club leagues supported (not int'l or national teams for now); sorry" unless league.national? && league.clubs?
30
+
31
+ ### calc countries
32
+ ### uses "global" func in sports-catalogs for now
33
+ ## move code here - why? why not?
34
+ country = find_countries_for_league( league )
35
+ _search.match_by( name: name,
36
+ country: country )
37
+ else
38
+ _search.match_by( name: name,
39
+ country: country )
40
+ end
41
+ end
42
+
43
+
44
+ ## todo/fix/check: use rename to find_canon or find_canonical() or something??
45
+ ## remove (getting used?) - why? why not?
46
+ # def []( name ) ## lookup by canoncial name only; todo/fix: add find alias why? why not?
47
+ # puts "WARN!! do not use ClubIndex#[] for lookup >#{name}< - will get removed!!!"
48
+ # @clubs[ name ]
49
+ # end
50
+
51
+
52
+ ###############
53
+ ### more deriv support functions / helpers
54
+ def self.match( name ) match_by( name: name ); end
55
+
56
+ ##########
57
+ # "legacy" finders - return zero or one club
58
+ ## (if more than one match, exit/raise error/exception)
59
+ def self.find( name ) find_by( name: name ); end
60
+ def self.find!( name ) find_by!( name: name ); end
61
+
62
+ ## find - always returns a single record / match or nil
63
+ ## if there is more than one match than find aborts / fails
64
+ def self.find_by!( name:, country: nil,
65
+ league: nil ) ## todo/fix: add international or league flag?
66
+ club = find_by( name: name,
67
+ country: country,
68
+ league: league )
69
+
70
+ if club.nil?
71
+ puts "** !!! ERROR - no match for club >#{name}<"
72
+ exit 1
73
+ end
74
+
75
+ club
76
+ end
77
+
78
+
79
+ def self.find_by( name:, country: nil,
80
+ league: nil ) ## todo/fix: add international or league flag?
81
+ ## note: allow passing in of country key too (auto-counvert)
82
+ ## and country struct too
83
+ ## - country assumes / allows the country key or fifa code for now
84
+ recs = match_by( name: name,
85
+ country: country,
86
+ league: league )
87
+
88
+ club = nil
89
+ if recs.empty?
90
+ ## puts "** !!! WARN !!! no match for club >#{name}<"
91
+ elsif recs.size > 1
92
+ puts "** !!! ERROR - too many matches (#{recs.size}) for club >#{name}<:"
93
+ pp recs
94
+ exit 1
95
+ else # bingo; match - assume size == 1
96
+ club = recs[0]
97
+ end
98
+
99
+ club
100
+ end
101
+
102
+
103
+ #######
104
+ # more support methods
105
+ def self.build_mods( mods )
106
+ ## e.g.
107
+ ## { 'Arsenal | Arsenal FC' => 'Arsenal, ENG',
108
+ ## 'Liverpool | Liverpool FC' => 'Liverpool, ENG',
109
+ ## 'Barcelona' => 'Barcelona, ESP',
110
+ ## 'Valencia' => 'Valencia, ESP' }
111
+
112
+ mods.reduce({}) do |h,(club_names, club_line)|
113
+
114
+ values = club_line.split( ',' )
115
+ values = values.map { |value| value.strip } ## strip all spaces
116
+
117
+ ## todo/fix: make sure country is present !!!!
118
+ club_name, country_name = values
119
+ club = find_by!( name: club_name, country: country_name )
120
+
121
+ values = club_names.split( '|' )
122
+ values = values.map { |value| value.strip } ## strip all spaces
123
+
124
+ values.each do |club_name|
125
+ h[club_name] = club
126
+ end
127
+ h
128
+ end
129
+ end
130
+ end # class Club
131
+
132
+ end # module Sports
@@ -0,0 +1,50 @@
1
+ module Sports
2
+
3
+ class EventInfo
4
+ def self._search() CatalogDb::Metal::EventInfo; end
5
+
6
+ def self.find_by( league:, season: )
7
+ _search.find_by( league: league,
8
+ season: season )
9
+ end
10
+
11
+ def self.seasons( league )
12
+ _search.seasons( league )
13
+ end
14
+ end # class EventInfo
15
+
16
+ end # module Sports
17
+
18
+
19
+ __END__
20
+
21
+
22
+ ####
23
+ ## virtual table for season lookup
24
+ ## note - use EventSeaon to avoid name conflict with (global) Season class
25
+ ## find a better name SeasonInfo or SeasonFinder or SeasonStore
26
+ ## or SeasonQ or ??
27
+ class EventSeasonSearch
28
+ def initialize( events: )
29
+ @events = events
30
+ end
31
+
32
+ ###############
33
+ ## todo/fix: find a better algo to guess season for date!!!
34
+ ##
35
+ def find_by( date:, league: )
36
+ date = Date.strptime( date, '%Y-%m-%d' ) if date.is_a?( String )
37
+
38
+ infos = @events.seasons( league )
39
+
40
+ infos.each do |info|
41
+ return info.season if info.include?( date )
42
+ end
43
+ nil
44
+ end
45
+ end # class EventSeasonSearch
46
+ end # class SportSearch
47
+
48
+
49
+
50
+
@@ -0,0 +1,16 @@
1
+ ###
2
+ # more to be done - is a dummy for now !!!!!
3
+
4
+
5
+ module Sports
6
+ class Team
7
+ ## add convenience lookup helper / method for name by season for now
8
+ ## use clubs history - for now kept separate from struct - why? why not?
9
+ def name_by_season( season )
10
+ ## note: returns / fallback to "regular" name if no records found in history
11
+ club_history = SportDb::Import.catalog.clubs_history
12
+ club_history.find_name_by( name: name, season: season ) || name
13
+ end
14
+ end # class Team
15
+ end # module Sports
16
+
@@ -0,0 +1,56 @@
1
+
2
+ module Sports
3
+
4
+ class League
5
+ def self._search() CatalogDb::Metal::League; end
6
+
7
+ ###################
8
+ ## core required delegates - use delegate generator - why? why not?
9
+ def self.match_by( name: nil, code: nil, country: nil )
10
+ ## todo/fix upstream - remove "generic" match_by() - why? why not?
11
+ ###
12
+ if code && name.nil?
13
+ _search.match_by_code( code, country: country )
14
+ elsif name && code.nil?
15
+ _search.match_by_name( name, country: country )
16
+ else
17
+ raise ArgumentError, "LeagueSearch#match_by - one (and only one arg) required - code: or name:"
18
+ end
19
+ end
20
+
21
+
22
+ ## all-in-one query (name or code)
23
+ def self.match( q, country: nil )
24
+ _search.match_by_name_or_code( q, country: country )
25
+ end
26
+
27
+ ###############
28
+ ### more deriv support functions / helpers
29
+ def self.find!( q )
30
+ league = find( q )
31
+ if league.nil?
32
+ puts "** !!! ERROR - no league match found for >#{q}<, add to leagues table; sorry"
33
+ exit 1
34
+ end
35
+ league
36
+ end
37
+
38
+ def self.find( q )
39
+ league = nil
40
+ recs = match( q )
41
+ # pp m
42
+
43
+ if recs.empty?
44
+ ## fall through/do nothing
45
+ elsif recs.size > 1
46
+ puts "** !!! ERROR - too many matches (#{recs.size}) for league #{q}:"
47
+ pp recs
48
+ exit 1
49
+ else
50
+ league = recs[0]
51
+ end
52
+
53
+ league
54
+ end
55
+ end # class League
56
+ end # module Sports
@@ -0,0 +1,40 @@
1
+
2
+ module Sports
3
+
4
+ class Ground
5
+ def self._search() CatalogDb::Metal::Ground; end
6
+
7
+ ###################
8
+ ## core required delegates - use delegate generator - why? why not?
9
+ def self.match_by( name:, country: nil, city: nil )
10
+ _search.match_by( name: name,
11
+ country: country,
12
+ city: city )
13
+ end
14
+
15
+ ###############
16
+ ### more deriv support functions / helpers
17
+ def self.match( name ) match_by( name: name ); end
18
+ ## add more here - why? why not?
19
+ end # class Ground
20
+
21
+
22
+
23
+ class Player
24
+ def self._search() CatalogDb::Metal::Player; end
25
+
26
+ ###################
27
+ ## core required delegates - use delegate generator - why? why not?
28
+ def self.match_by( name:, country: nil, year: nil )
29
+ _search.match_by( name: name,
30
+ country: country,
31
+ year: year )
32
+ end
33
+
34
+ ###############
35
+ ### more deriv support functions / helpers
36
+ def self.match( name ) match_by( name: name ); end
37
+ ## add more here - why? why not?
38
+ end # class Player
39
+
40
+ end # module Sports
@@ -0,0 +1,98 @@
1
+
2
+ module Sports
3
+
4
+
5
+ class NationalTeam
6
+ def self._search() CatalogDb::Metal::NationalTeam; end
7
+
8
+ ###################
9
+ ## core required delegates - use delegate generator - why? why not?
10
+
11
+
12
+ ### todo/fix
13
+ ### upstream only support/require find !!!!!!!
14
+
15
+ ## change core api to match( q ) only - why? why not?
16
+ ## and make find and find! derivs???
17
+ def self.find( q ) _search.find( q ); end
18
+ def self.find!( q ) _search.find!( q ); end
19
+ end # class NationalTeam
20
+
21
+
22
+ class Team
23
+ ## note: "virtual" index lets you search clubs and/or national_teams (don't care)
24
+
25
+ ## todo/check: rename to/use map_by! for array version - why? why not?
26
+ def self.find_by!( name:, league:, mods: nil )
27
+ if name.is_a?( Array )
28
+ recs = []
29
+ name.each do |q|
30
+ recs << _find_by!( name: q, league: league, mods: mods )
31
+ end
32
+ recs
33
+ else ## assume single name
34
+ _find_by!( name: name, league: league, mods: mods )
35
+ end
36
+ end
37
+
38
+
39
+ CLUB_NAME_RE = %r{^
40
+ (?<name>[^()]+?) ## non-greedy
41
+ (?:
42
+ \s+
43
+ \(
44
+ (?<code>[A-Z][A-Za-z]{2,3}) ## optional (country) code; support single code e.g. (A) - why? why not?
45
+ \)
46
+ )?
47
+ $}x ## note - allow (URU) and (Uru) - why? why not
48
+
49
+
50
+ ###
51
+ # note: missing teams will get
52
+ ## auto-created if possible
53
+ ## only ambigious results (too many matches) raise expection!!!
54
+ def self._find_by!( name:, league:, mods: nil )
55
+ if mods && mods[ league.key ] && mods[ league.key ][ name ]
56
+ mods[ league.key ][ name ]
57
+ else
58
+ if league.clubs?
59
+ if league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
60
+ ###
61
+ ## get country code from name
62
+ ## e.g. Liverpool FC (ENG) or
63
+ ## Liverpool FC (URU) etc.
64
+
65
+ ## check for country code
66
+ if m=CLUB_NAME_RE.match( name )
67
+ if m[:code]
68
+ Club.find_by!( name: m[:name],
69
+ country: m[:code] )
70
+ else
71
+ Club.find!( name )
72
+ end
73
+ else
74
+ puts "!! PARSE ERROR - invalid club name; cannot match with CLUB_NAME_RE >#{team}<"
75
+ exit 1
76
+ end
77
+ else ## assume clubs in domestic/national league tournament
78
+ ## note - search by league countries (may incl. more than one country
79
+ ## e.g. us incl. ca, fr incl. mc, ch incl. li, etc.
80
+ rec = Club.find_by( name: name, league: league )
81
+ if rec.nil?
82
+ puts "auto-create (missing) club #{name}"
83
+ ## todo/fix: add auto flag!!!!
84
+ ### like in rounds!!!
85
+ ## to track auto-created clubs
86
+ rec = Club.new( name: name )
87
+ rec.country = league.country ## fix: country kwarg not yet supported!!
88
+ pp rec
89
+ end
90
+ rec
91
+ end
92
+ else ## assume national teams (not clubs)
93
+ NationalTeam.find!( name )
94
+ end
95
+ end
96
+ end # method _find_by!
97
+ end # class Team
98
+ end # module Sports
@@ -2,8 +2,8 @@ module SportDb
2
2
  module Module
3
3
  module Search
4
4
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
5
- MINOR = 1
6
- PATCH = 1
5
+ MINOR = 2
6
+ PATCH = 0
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version
@@ -1,9 +1,8 @@
1
1
  module Sports
2
2
 
3
+
3
4
  class City
4
- def self._search #### use service/api or such - why? why not?
5
- SportDb::Import.world.cities
6
- end
5
+ def self._search() CatalogDb::Metal::City; end
7
6
 
8
7
  def self.match_by( name: )
9
8
  _search.match_by( name: name )
@@ -12,9 +11,7 @@ end # class City
12
11
 
13
12
 
14
13
  class Country
15
- def self._search #### use service/api or such - why? why not?
16
- SportDb::Import.world.countries
17
- end
14
+ def self._search() CatalogDb::Metal::Country; end
18
15
 
19
16
  def self.find_by( code: nil, name: nil )
20
17
  ## todo/fix upstream - change to find_by( code:, name:, ) too - why? why not?
@@ -23,7 +20,7 @@ class Country
23
20
  elsif name && code.nil?
24
21
  _search.find_by_name( name )
25
22
  else
26
- raise ArgumentError, "CountrySearch#find_by - one (and only one arg) required - code: or name:"
23
+ raise ArgumentError, "Country#find_by - one (and only one arg) required - code: or name:"
27
24
  end
28
25
  end
29
26
 
@@ -3,110 +3,27 @@ require 'sportdb/catalogs'
3
3
 
4
4
 
5
5
 
6
- ## move to base?
7
- ### shared basics for search
8
- class SportSearch
9
- class Search ## base search service - use/keep - why? why not?
10
- attr_reader :service
11
- def initialize( service ) @service = service; end
12
- end # class Search
13
- end
14
-
15
-
16
-
17
-
18
6
  ## our own code
19
7
  require_relative 'search/version'
20
- require_relative 'search/sport'
21
-
22
-
23
- ########
24
- ### setup sport search apis
25
- class SportSearch
26
- def initialize( leagues:,
27
- national_teams:,
28
- clubs:,
29
- grounds:,
30
- events:,
31
- players:
32
- )
33
- @leagues = LeagueSearch.new( leagues )
34
- @national_teams = NationalTeamSearch.new( national_teams )
35
- @clubs = ClubSearch.new( clubs )
36
- @events = EventSearch.new( events )
37
-
38
- @grounds = GroundSearch.new( grounds )
39
-
40
- @players = PlayerSearch.new( players )
41
-
42
- ## virtual deriv ("composite") search services
43
- @teams = TeamSearch.new( clubs: @clubs,
44
- national_teams: @national_teams )
45
- @event_seasons = EventSeasonSearch.new( events: @events )
46
8
 
47
- end
48
-
49
- def leagues() @leagues; end
50
- def national_teams() @national_teams; end
51
- def clubs() @clubs; end
52
- def events() @events; end
53
- def grounds() @grounds; end
54
-
55
- def players() @players; end
56
-
57
- def teams() @teams; end ## note - virtual table
58
- def seasons() @event_seasons; end ## note - virtual table
59
-
60
- def countries
61
- puts
62
- puts "[WARN] do NOT use catalog.countries, deprecated!!!"
63
- puts " please, switch to new world.countries search service"
64
- puts
65
- exit 1
66
- end
67
- end # class SportSearch
68
-
69
-
70
- class WorldSearch
71
- def initialize( countries:, cities: )
72
- ## change service to country_service or such - why? why not?
73
- ## add city_service and such later
74
-
75
- @countries = countries
76
- @cities = cities
77
- end
9
+ ###
10
+ ## add/augment core classes with search services
11
+ require_relative 'search/world'
12
+ require_relative 'search/sport-leagues'
13
+ require_relative 'search/sport-clubs'
14
+ require_relative 'search/sport-teams'
15
+ require_relative 'search/sport-events'
16
+ require_relative 'search/sport-more'
78
17
 
79
- ####
80
- # note: for now setup only for countries & cities
81
- def countries() @countries; end
82
- def cities() @cities; end
83
- end # class WorldSearch
18
+ ##
19
+ ## require_relative 'search/sport-history' ## to be done
84
20
 
85
21
 
86
22
  #######
87
23
  ## add configuration
88
-
89
24
  module SportDb
90
25
  module Import
91
26
  class Configuration
92
- def world
93
- @world ||= WorldSearch.new(
94
- countries: CatalogDb::Metal::Country,
95
- cities: CatalogDb::Metal::City,
96
- )
97
- end
98
-
99
- def catalog
100
- @catalog ||= SportSearch.new(
101
- leagues: CatalogDb::Metal::League,
102
- national_teams: CatalogDb::Metal::NationalTeam,
103
- clubs: CatalogDb::Metal::Club,
104
- grounds: CatalogDb::Metal::Ground,
105
- events: CatalogDb::Metal::EventInfo,
106
- players: CatalogDb::Metal::Player, # note - via players.db !!!
107
- )
108
- end
109
-
110
27
  ###
111
28
  # find a better name for setting - why? why not?
112
29
  # how about catalogdb or ???
@@ -131,18 +48,12 @@ class Configuration
131
48
 
132
49
  @players_path
133
50
  end
134
-
135
-
136
51
  end # class Configuration
137
52
 
138
- ## e.g. use config.catalog -- keep Import.catalog as a shortcut (for "read-only" access)
139
- def self.world() config.world; end
140
- def self.catalog() config.catalog; end
141
-
142
- ## lets you use
143
- ## SportDb::Import.configure do |config|
144
- ## config.catalog_path = './catalog.db'
145
- ## end
53
+ ## lets you use
54
+ ## SportDb::Import.configure do |config|
55
+ ## config.catalog_path = './catalog.db'
56
+ ## end
146
57
  def self.configure() yield( config ); end
147
58
  def self.config() @config ||= Configuration.new; end
148
59
  end # module Import
@@ -160,28 +71,6 @@ end # module Sports
160
71
 
161
72
 
162
73
 
163
-
164
- ###
165
- ## add/augment core classes with search services
166
- require_relative 'search/structs'
167
- require_relative 'search/structs_world'
168
-
169
-
170
-
171
- module SportDb
172
- module Import
173
- class Team
174
- ## add convenience lookup helper / method for name by season for now
175
- ## use clubs history - for now kept separate from struct - why? why not?
176
- def name_by_season( season )
177
- ## note: returns / fallback to "regular" name if no records found in history
178
- SportDb::Import.catalog.clubs_history.find_name_by( name: name, season: season ) || name
179
- end
180
- end # class Team
181
- end # module Import
182
- end # module SportDb
183
-
184
-
185
74
  ###
186
75
  ## add default/built-in catalog here - why? why not?
187
76
  ## todo/fix - set catalog_path on demand
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sportdb-search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-30 00:00:00.000000000 Z
11
+ date: 2024-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sportdb-catalogs
@@ -60,7 +60,8 @@ dependencies:
60
60
  version: '4.1'
61
61
  description: sportdb-search - find national teams, clubs, leagues & more
62
62
  email: gerald.bauer@gmail.com
63
- executables: []
63
+ executables:
64
+ - fbq
64
65
  extensions: []
65
66
  extra_rdoc_files:
66
67
  - CHANGELOG.md
@@ -71,11 +72,16 @@ files:
71
72
  - Manifest.txt
72
73
  - README.md
73
74
  - Rakefile
75
+ - bin/fbq
74
76
  - lib/sportdb/search.rb
75
- - lib/sportdb/search/sport.rb
76
- - lib/sportdb/search/structs.rb
77
- - lib/sportdb/search/structs_world.rb
77
+ - lib/sportdb/search/sport-clubs.rb
78
+ - lib/sportdb/search/sport-events.rb
79
+ - lib/sportdb/search/sport-history.rb
80
+ - lib/sportdb/search/sport-leagues.rb
81
+ - lib/sportdb/search/sport-more.rb
82
+ - lib/sportdb/search/sport-teams.rb
78
83
  - lib/sportdb/search/version.rb
84
+ - lib/sportdb/search/world.rb
79
85
  homepage: https://github.com/sportdb/sport.db
80
86
  licenses:
81
87
  - Public Domain
@@ -1,384 +0,0 @@
1
- ###
2
- # sport search service api for leagues, clubs and more
3
- #
4
- # core api is:
5
-
6
-
7
- class SportSearch
8
-
9
- class PlayerSearch < Search
10
- ###################
11
- ## core required delegates - use delegate generator - why? why not?
12
- def match_by( name:, country: nil, year: nil )
13
- @service.match_by( name: name,
14
- country: country,
15
- year: year )
16
- end
17
-
18
- ###############
19
- ### more deriv support functions / helpers
20
- def match( name ) match_by( name: name ); end
21
- ## add more here - why? why not?
22
- end # class PlayerSearch
23
-
24
-
25
- class LeagueSearch < Search
26
-
27
- ###################
28
- ## core required delegates - use delegate generator - why? why not?
29
- def match_by( name: nil, code: nil, country: nil )
30
- ## todo/fix upstream - remove "generic" match_by() - why? why not?
31
- ###
32
- if code && name.nil?
33
- @service.match_by_code( code, country: country )
34
- elsif name && code.nil?
35
- @service.match_by_name( name, country: country )
36
- else
37
- raise ArgumentError, "LeagueSearch#match_by - one (and only one arg) required - code: or name:"
38
- end
39
- ## @service.match_by( name: name,
40
- ## country: country )
41
- end
42
-
43
- ## all-in-one query (name or code)
44
- def match( q, country: nil )
45
- @service.match_by_name_or_code( q, country: country )
46
- end
47
-
48
-
49
- ###############
50
- ### more deriv support functions / helpers
51
- def find!( q )
52
- league = find( q )
53
- if league.nil?
54
- puts "** !!! ERROR - no league match found for >#{q}<, add to leagues table; sorry"
55
- exit 1
56
- end
57
- league
58
- end
59
-
60
- def find( q )
61
- league = nil
62
- recs = match( q )
63
- # pp m
64
-
65
- if recs.empty?
66
- ## fall through/do nothing
67
- elsif recs.size > 1
68
- puts "** !!! ERROR - ambigious league query; too many leagues (#{recs.size}) found:"
69
- pp recs
70
- exit 1
71
- else
72
- league = recs[0]
73
- end
74
-
75
- league
76
- end
77
- end # class LeagueSearch
78
-
79
-
80
-
81
- class GroundSearch < Search
82
- ###################
83
- ## core required delegates - use delegate generator - why? why not?
84
- def match_by( name:, country: nil, city: nil )
85
- @service.match_by( name: name,
86
- country: country,
87
- city: city )
88
- end
89
-
90
- ###############
91
- ### more deriv support functions / helpers
92
- def match( name ) match_by( name: name ); end
93
- ## add more here - why? why not?
94
- end # class GroundSearch
95
-
96
-
97
-
98
- class NationalTeamSearch < Search
99
- ###################
100
- ## core required delegates - use delegate generator - why? why not?
101
-
102
- ## changle core api to match( q ) only - why? why not?
103
- ## and make find and find! derivs???
104
- def find( q ) @service.find( q ); end
105
- def find!( q ) @service.find!( q ); end
106
- end # class NationalTeamSearch
107
-
108
-
109
- class ClubSearch < Search
110
- ###################
111
- ## core required delegates - use delegate generator - why? why not?
112
-
113
- ## add mods here - why? why not?
114
-
115
- def match_by( name:, country: nil,
116
- league: nil,
117
- mods: nil )
118
- ## for now assume "global" mods - checks only for name
119
- ##
120
- if mods && mods[ name ]
121
- club = mods[ name ]
122
- return [club] # note: wrap (single record) in array
123
- end
124
-
125
- ## note: add "auto-magic" country calculation via league record
126
- ## if league is a national league for football clubs
127
- if league
128
- raise ArgumentError, "match_by - league AND country NOT supported; sorry" if country
129
- ### find countries via league
130
- ### support league.intl? too - why? why not?
131
- ### or only nationa league
132
- raise ArgumentError, "match_by - league - only national club leagues supported (not int'l or national teams for now); sorry" unless league.national? && league.clubs?
133
-
134
- ### calc countries
135
- ### uses "global" func in sports-catalogs for now
136
- ## move code here - why? why not?
137
- country = find_countries_for_league( league )
138
- @service.match_by( name: name,
139
- country: country )
140
- else
141
- @service.match_by( name: name,
142
- country: country )
143
- end
144
- end
145
-
146
-
147
- ## todo/fix/check: use rename to find_canon or find_canonical() or something??
148
- ## remove (getting used?) - why? why not?
149
- # def []( name ) ## lookup by canoncial name only; todo/fix: add find alias why? why not?
150
- # puts "WARN!! do not use ClubIndex#[] for lookup >#{name}< - will get removed!!!"
151
- # @clubs[ name ]
152
- # end
153
-
154
-
155
- ###############
156
- ### more deriv support functions / helpers
157
- def match( name ) match_by( name: name ); end
158
-
159
- ##########
160
- # "legacy" finders - return zero or one club
161
- ## (if more than one match, exit/raise error/exception)
162
- def find( name ) find_by( name: name ); end
163
- def find!( name ) find_by!( name: name ); end
164
-
165
- ## find - always returns a single record / match or nil
166
- ## if there is more than one match than find aborts / fails
167
- def find_by!( name:, country: nil,
168
- league: nil ) ## todo/fix: add international or league flag?
169
- club = find_by( name: name,
170
- country: country,
171
- league: league )
172
-
173
- if club.nil?
174
- puts "** !!! ERROR - no match for club >#{name}<"
175
- exit 1
176
- end
177
-
178
- club
179
- end
180
-
181
-
182
- def find_by( name:, country: nil,
183
- league: nil ) ## todo/fix: add international or league flag?
184
- ## note: allow passing in of country key too (auto-counvert)
185
- ## and country struct too
186
- ## - country assumes / allows the country key or fifa code for now
187
- recs = match_by( name: name,
188
- country: country,
189
- league: league )
190
-
191
- club = nil
192
- if recs.empty?
193
- ## puts "** !!! WARN !!! no match for club >#{name}<"
194
- elsif recs.size > 1
195
- puts "** !!! ERROR - too many matches (#{recs.size}) for club >#{name}<:"
196
- pp recs
197
- exit 1
198
- else # bingo; match - assume size == 1
199
- club = recs[0]
200
- end
201
-
202
- club
203
- end
204
-
205
-
206
- #######
207
- # more support methods
208
- def build_mods( mods )
209
- ## e.g.
210
- ## { 'Arsenal | Arsenal FC' => 'Arsenal, ENG',
211
- ## 'Liverpool | Liverpool FC' => 'Liverpool, ENG',
212
- ## 'Barcelona' => 'Barcelona, ESP',
213
- ## 'Valencia' => 'Valencia, ESP' }
214
-
215
- mods.reduce({}) do |h,(club_names, club_line)|
216
-
217
- values = club_line.split( ',' )
218
- values = values.map { |value| value.strip } ## strip all spaces
219
-
220
- ## todo/fix: make sure country is present !!!!
221
- club_name, country_name = values
222
- club = find_by!( name: club_name, country: country_name )
223
-
224
- values = club_names.split( '|' )
225
- values = values.map { |value| value.strip } ## strip all spaces
226
-
227
- values.each do |club_name|
228
- h[club_name] = club
229
- end
230
- h
231
- end
232
- end
233
- end # class ClubSearch
234
-
235
-
236
-
237
- ## todo/check - change to EventInfoSearch - why? why not?
238
- class EventSearch < Search
239
- ##
240
- ## todo - eventinfo search still open / up for change
241
-
242
- ###################
243
- ## core required delegates - use delegate generator - why? why not?
244
- def seasons( league )
245
- @service.seasons( league )
246
- end
247
- def find_by( league:, season: )
248
- @service.find_by( league: league,
249
- season: season )
250
- end
251
- end # class EventSearch
252
-
253
-
254
- ####
255
- ## virtual table for season lookup
256
- ## note - use EventSeaon to avoid name conflict with (global) Season class
257
- ## find a better name SeasonInfo or SeasonFinder or SeasonStore
258
- ## or SeasonQ or ??
259
- class EventSeasonSearch
260
- def initialize( events: )
261
- @events = events
262
- end
263
-
264
- ###############
265
- ## todo/fix: find a better algo to guess season for date!!!
266
- ##
267
- def find_by( date:, league: )
268
- date = Date.strptime( date, '%Y-%m-%d' ) if date.is_a?( String )
269
-
270
- infos = @events.seasons( league )
271
-
272
- infos.each do |info|
273
- return info.season if info.include?( date )
274
- end
275
- nil
276
- end
277
- end # class EventSeasonSearch
278
-
279
-
280
- ######
281
- ### add virtual team search ( clubs + national teams)
282
- ## note: no record base!!!!!
283
- class TeamSearch
284
- ## note: "virtual" index lets you search clubs and/or national_teams (don't care)
285
-
286
- def initialize( clubs:, national_teams: )
287
- @clubs = clubs
288
- @national_teams = national_teams
289
- end
290
-
291
- ## todo/check: rename to/use map_by! for array version - why? why not?
292
- def find_by!( name:, league:, mods: nil )
293
- if name.is_a?( Array )
294
- recs = []
295
- name.each do |q|
296
- recs << _find_by!( name: q, league: league, mods: mods )
297
- end
298
- recs
299
- else ## assume single name
300
- _find_by!( name: name, league: league, mods: mods )
301
- end
302
- end
303
-
304
-
305
-
306
- CLUB_NAME_RE = %r{^
307
- (?<name>[^()]+?) ## non-greedy
308
- (?:
309
- \s+
310
- \(
311
- (?<code>[A-Z][A-Za-z]{2,3}) ## optional (country) code; support single code e.g. (A) - why? why not?
312
- \)
313
- )?
314
- $}x ## note - allow (URU) and (Uru) - why? why not
315
-
316
-
317
- ###
318
- # note: missing teams will get
319
- ## auto-created if possible
320
- ## only ambigious results (too many matches) raise expection!!!
321
- def _find_by!( name:, league:, mods: nil )
322
- if mods && mods[ league.key ] && mods[ league.key ][ name ]
323
- mods[ league.key ][ name ]
324
- else
325
- if league.clubs?
326
- if league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
327
- ###
328
- ## get country code from name
329
- ## e.g. Liverpool FC (ENG) or
330
- ## Liverpool FC (URU) etc.
331
-
332
- ## check for country code
333
- if m=CLUB_NAME_RE.match( name )
334
- if m[:code]
335
- @clubs.find_by!( name: m[:name],
336
- country: m[:code] )
337
- else
338
- @clubs.find!( name )
339
- end
340
- else
341
- puts "!! PARSE ERROR - invalid club name; cannot match with CLUB_NAME_RE >#{team}<"
342
- exit 1
343
- end
344
- else ## assume clubs in domestic/national league tournament
345
- ## note - search by league countries (may incl. more than one country
346
- ## e.g. us incl. ca, fr incl. mc, ch incl. li, etc.
347
- rec = @clubs.find_by( name: name, league: league )
348
- if rec.nil?
349
- puts "auto-create (missing) club #{name}"
350
- ## todo/fix: add auto flag!!!!
351
- ### like in rounds!!!
352
- ## to track auto-created clubs
353
- rec = SportDb::Import::Club.new( name: name )
354
- rec.country = league.country ## fix: country kwarg not yet supported!!
355
- pp rec
356
- end
357
- rec
358
- end
359
- else ## assume national teams (not clubs)
360
- @national_teams.find!( name )
361
- end
362
- end
363
- end # method _find_by!
364
-
365
-
366
- def _find_by_v0!( name:, league:, mods: nil )
367
- if mods && mods[ league.key ] && mods[ league.key ][ name ]
368
- mods[ league.key ][ name ]
369
- else
370
- if league.clubs?
371
- if league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
372
- @clubs.find!( name )
373
- else ## assume clubs in domestic/national league tournament
374
- ## note - search by league countries (may incl. more than one country
375
- ## e.g. us incl. ca, fr incl. mc, ch incl. li, etc.
376
- @clubs.find_by!( name: name, league: league )
377
- end
378
- else ## assume national teams (not clubs)
379
- @national_teams.find!( name )
380
- end
381
- end
382
- end # method _find_by!
383
- end # class TeamSearch
384
- end # class SportSearch
@@ -1,118 +0,0 @@
1
- ###
2
- # note - extend all structs for with search api
3
- #
4
- # todo - add more helpers!!!!
5
- #
6
- #
7
- # todo - fix - move all non-core search functionality/machinery
8
- # over here - why? why not?
9
-
10
-
11
-
12
- module Sports
13
- ###
14
- ## todo/fix - add find_by( code: ), find_by( name: )
15
- ## split - why? why not?
16
-
17
-
18
- class League
19
- def self._search #### use service/api or such - why? why not?
20
- SportDb::Import.catalog.leagues
21
- end
22
- def self.match_by( name: nil, code: nil,
23
- country: nil )
24
- _search.match_by( name: name, code: code,
25
- country: country )
26
- end
27
- def self.match( q, country: nil )
28
- _search.match( q, country: country )
29
- end
30
-
31
- def self.find!( q ) _search.find!( q ); end
32
- def self.find( q ) _search_find( q ); end
33
- end # class League
34
-
35
-
36
- class NationalTeam
37
- def self._search #### use service/api or such - why? why not?
38
- SportDb::Import.catalog.national_teams
39
- end
40
-
41
- def self.find( q ) _search.find( q ); end
42
- def self.find!( q ) _search_find!( q ); end
43
- end # class NationalTeam
44
-
45
-
46
-
47
- class Club
48
- def self._search #### use service/api or such - why? why not?
49
- SportDb::Import.catalog.clubs
50
- end
51
-
52
- def self.match_by( name:, country: nil,
53
- league: nil,
54
- mods: nil )
55
- _search.match_by( name: name, country: country,
56
- league: league, mods: mods )
57
- end
58
- def self.match( name ) match_by( name: name ); end
59
-
60
- def self.find( name ) _search.find_by( name: name ); end
61
- def self.find!( name ) _search.find_by!( name: name ); end
62
-
63
- def self.find_by!( name:, country: nil,
64
- league: nil )
65
- _search.find_by!( name: name, country: country,
66
- league: league )
67
- end
68
- def self.find_by( name:, country: nil,
69
- league: nil )
70
- _search.find_by( name: name, country: country,
71
- league: league )
72
- end
73
-
74
- def self.build_mods( mods )
75
- _search.build_mods( mods )
76
- end
77
- end # class Club
78
-
79
-
80
- class Team
81
- def self._search
82
- SportDb::Import.catalog.teams
83
- end
84
-
85
- ## todo/check: rename to/use map_by! for array version - why? why not?
86
- def self.find_by!( name:, league:, mods: nil )
87
- _search.find_by!( name: name,
88
- league: league,
89
- mods: mods )
90
- end
91
- end # class Team
92
-
93
-
94
- class EventInfo
95
- def self._search
96
- SportDb::Import.catalog.events
97
- end
98
-
99
- def self.find_by( league:, season: )
100
- _search.find_by( league: league,
101
- season: season )
102
- end
103
- end # class EventInfo
104
-
105
-
106
-
107
- class Ground
108
- def self._search
109
- SportDb::Import.catalog.grounds
110
- end
111
-
112
- def self.match_by( name:, country: nil, city: nil )
113
- _search.match_by( name: name,
114
- country: country,
115
- city: city )
116
- end
117
- end # class Ground
118
- end # module Sports