sportdb-search 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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