sportdb-readers 1.2.0 → 2.0.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: 40a0db453b48c9bed47b6370c44249d5c8197df2fddc90a911653f2ddd145357
4
- data.tar.gz: 8792558c03345ba1662249e5239f5b6122324740f353be4143c420f482486742
3
+ metadata.gz: cdad0e950d6144fe9de65be3f4a87057b05e1f03e168237c44c5f0c9f2d3f0b3
4
+ data.tar.gz: 541e0de0a923297dc7b1b7349319e77bcaa31ac5405bb3f2cdd111f522230985
5
5
  SHA512:
6
- metadata.gz: 342de18d45ec3be01a528be68e2e77d18866911cef7fbad17424179c648eda816170e77ea39987bfa66b48af9627284080535eab292c0e9798e3ca25ad09118a
7
- data.tar.gz: 5861f675107c90fdd10f865396df5b43f30df1df63ff3e437f409612853b393d575f96df0b59a86d23c2773791a2a63d57db1df9949ebb4e0845768b088a9654
6
+ metadata.gz: d36e0affcfc3fbdd5fbb50fabcbaebce34935f48a257c7ab7e918cf2cf1a96bdf3b7d2737f05adf75f9094ade927c88806d1fef5ce9d792feaac852534125e86
7
+ data.tar.gz: 797fd4250e078bf2255e38830358cedeea761ea85236e7084f08fd04ba71b2831890267c7a2268cc92312556149dda33a7ef893d0a5878ce9474be221ba50d9b
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 1.2.0
1
+ ### 2.0.0
2
2
 
3
3
  ### 0.0.1 / 2019-10-29
4
4
 
data/Manifest.txt CHANGED
@@ -7,4 +7,11 @@ lib/sportdb/readers.rb
7
7
  lib/sportdb/readers/conf_reader.rb
8
8
  lib/sportdb/readers/match_reader.rb
9
9
  lib/sportdb/readers/package.rb
10
+ lib/sportdb/readers/sync/club.rb
11
+ lib/sportdb/readers/sync/country.rb
12
+ lib/sportdb/readers/sync/event.rb
13
+ lib/sportdb/readers/sync/league.rb
14
+ lib/sportdb/readers/sync/match.rb
15
+ lib/sportdb/readers/sync/more.rb
16
+ lib/sportdb/readers/sync/season.rb
10
17
  lib/sportdb/readers/version.rb
data/README.md CHANGED
@@ -50,15 +50,14 @@ Matchday 1
50
50
  ...
51
51
  ```
52
52
 
53
- (Source: [england/2015-16/1-premierleague-i.txt](https://github.com/openfootball/england/blob/master/2015-16/1-premierleague-i.txt))
53
+ (Source: [england/2015-16/1-premierleague.txt](https://github.com/openfootball/england/blob/master/2015-16/1-premierleague.txt))
54
54
 
55
55
  and let's try:
56
56
 
57
57
  ``` ruby
58
58
  ## assumes football.db datasets for England in ./england directory
59
59
  ## see github.com/openfootball/england
60
- SportDb.read( './england/2015-16/1-premierleague-i.txt' )
61
- SportDb.read( './england/2015-16/1-premierleague-ii.txt' )
60
+ SportDb.read( './england/2015-16/1-premierleague.txt' )
62
61
 
63
62
  ## let's try another season
64
63
  SportDb.read( './england/2019-20/1-premierleague.txt' )
@@ -136,7 +135,7 @@ Round, Date, Team 1, FT, HT, Team 2
136
135
  ```
137
136
  (Source: [england/2019-20/eng.1.csv](https://github.com/footballcsv/england/blob/master/2010s/2019-20/eng.1.csv))
138
137
 
139
- Yes, you can. See the [sportdb-importers library / gem »](https://github.com/sportdb/sport.db/tree/master/sportdb-importers)
138
+ Yes, you can. See the [sportdb-importers library / gem »](https://github.com/sportdb/sport.db/tree/master/sportdb-importers)
140
139
 
141
140
 
142
141
 
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require './lib/sportdb/readers/version.rb'
3
3
 
4
4
  Hoe.spec 'sportdb-readers' do
5
5
 
6
- self.version = SportDb::Readers::VERSION
6
+ self.version = SportDb::Module::Readers::VERSION
7
7
 
8
8
  self.summary = "sportdb-readers - sport.db readers for leagues, seasons, clubs, match schedules and results, and more"
9
9
  self.description = summary
@@ -20,11 +20,13 @@ Hoe.spec 'sportdb-readers' do
20
20
  self.licenses = ['Public Domain']
21
21
 
22
22
  self.extra_deps = [
23
- ['sportdb-sync', '>= 1.2.0'],
23
+ ['sportdb-formats', '>= 2.0.0'],
24
+ ['sportdb-catalogs', '>= 1.2.1'],
25
+ ['sportdb-models', '>= 2.1.0'],
24
26
  ]
25
27
 
26
28
  self.spec_extras = {
27
- required_ruby_version: '>= 2.2.2'
29
+ required_ruby_version: '>= 3.1.0'
28
30
  }
29
31
 
30
32
  end
@@ -1,8 +1,18 @@
1
1
 
2
2
  module SportDb
3
-
4
3
  class MatchReader ## todo/check: rename to MatchReaderV2 (use plural?) why? why not?
5
4
 
5
+
6
+ ### fix - remove catalog reference!!!
7
+ ## use classes with "augmented" static methods
8
+ ## e.g. Club.match_by etc.
9
+ def catalog
10
+ puts "[deprecated] do NOT use catalog reference; use classes with enhanced search static methods!"
11
+ Import.catalog
12
+ end
13
+
14
+
15
+
6
16
  def self.read( path, season: nil ) ## use - rename to read_file or from_file etc. - why? why not?
7
17
  txt = File.open( path, 'r:utf-8' ) {|f| f.read }
8
18
  parse( txt, season: season )
@@ -23,43 +33,22 @@ class MatchReader ## todo/check: rename to MatchReaderV2 (use plural?) why? w
23
33
  secs = LeagueOutlineReader.parse( @txt, season: season )
24
34
  pp secs
25
35
 
26
-
27
- ###
28
- ## todo/check/fix: move to LeagueOutlineReader for (re)use - why? why not?
29
- ## use sec[:lang] or something?
30
- langs = { ## map country keys to lang codes
31
- 'de' => 'de', ## de - Deutsch (German)
32
- 'at' => 'de',
33
- 'ch' => 'de',
34
- 'fr' => 'fr', ## fr - French
35
- 'it' => 'it', ## it - Italian
36
- 'es' => 'es', ## es - Español (Spanish)
37
- 'mx' => 'es',
38
- 'ar' => 'es', ## Argentina
39
- 'pt' => 'pt', ## pt - Português (Portuguese)
40
- 'br' => 'pt',
41
- }
42
-
43
36
  secs.each do |sec| ## sec(tion)s
44
37
  season = sec[:season]
45
38
  league = sec[:league]
46
39
  stage = sec[:stage]
47
40
  lines = sec[:lines]
48
41
 
49
- ## hack for now: switch lang
50
- ## todo/fix: set lang for now depending on league country!!!
51
- if league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
52
- Import.config.lang = 'en'
53
- else ## assume national/domestic
54
- Import.config.lang = langs[ league.country.key ] || 'en'
55
- end
56
-
57
42
  ### check if event info availabe - use start_date;
58
43
  ## otherwise we have to guess (use a "synthetic" start_date)
59
44
  event_info = catalog.events.find_by( season: season,
60
45
  league: league )
61
46
 
62
47
  start = if event_info && event_info.start_date
48
+ puts "event info found:"
49
+ puts " using start date from event: "
50
+ pp event_info
51
+ pp event_info.start_date
63
52
  event_info.start_date
64
53
  else
65
54
  if season.year?
@@ -70,61 +59,101 @@ class MatchReader ## todo/check: rename to MatchReaderV2 (use plural?) why? w
70
59
  end
71
60
 
72
61
 
73
- ### todo/check: make sure team include teams from group def too!!!!!
74
- ###
75
- ### note - only use teams and grounds for now
76
- auto_conf_teams,
77
- _rounds, _groups, _round_defs, _group_defs,
78
- auto_conf_grounds,
79
- _ = AutoConfParser.parse( lines, start: start )
80
-
81
-
62
+ parser = MatchParser.new( lines,
63
+ start ) ## note: keep season start_at date for now (no need for more specific stage date need for now)
64
+
65
+ auto_conf_teams, matches, rounds, groups = parser.parse
66
+
67
+ puts ">>> #{auto_conf_teams.size} teams:"
68
+ pp auto_conf_teams
69
+ puts ">>> #{matches.size} matches:"
70
+ ## pp matches
71
+ puts ">>> #{rounds.size} rounds:"
72
+ pp rounds
73
+ puts ">>> #{groups.size} groups:"
74
+ pp groups
75
+
76
+
77
+
82
78
  ## step 1: map/find teams
83
79
 
84
80
  ## note: loop over keys (holding the names); values hold the usage counter!! e.g. 'Arsenal' => 2, etc.
85
81
  mods = nil
86
82
  if league.clubs? && league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
83
+
84
+ ## quick hack - use "dynamic" keys for keys
85
+ uefa_el_q = Import::League.match_by( code: 'uefa.el.quali' )[0]
86
+ uefa_cl_q = Import::League.match_by( code: 'uefa.cl.quali' )[0]
87
+ uefa_cl = Import::League.match_by( code: 'uefa.cl' )[0]
88
+ uefa_el = Import::League.match_by( code: 'uefa.el' )[0]
89
+
90
+ pp [uefa_el_q, uefa_cl_q, uefa_cl, uefa_el]
91
+
87
92
  ### quick hack mods for popular/known ambigious club names
88
93
  ## todo/fix: make more generic / reuseable!!!!
89
94
  mods = {}
90
95
  ## europa league uses same mods as champions league
91
- mods[ 'uefa.el.quali' ] =
92
- mods[ 'uefa.cl.quali' ] =
93
- mods[ 'uefa.el' ] =
94
- mods[ 'uefa.cl' ] = catalog.clubs.build_mods(
96
+ mods[ uefa_el_q.key ] =
97
+ mods[ uefa_cl_q.key ] =
98
+ mods[ uefa_el.key ] =
99
+ mods[ uefa_cl.key ] = catalog.clubs.build_mods(
95
100
  { 'Liverpool | Liverpool FC' => 'Liverpool FC, ENG',
96
101
  'Arsenal | Arsenal FC' => 'Arsenal FC, ENG',
97
102
  'Barcelona' => 'FC Barcelona, ESP',
98
- 'Valencia' => 'Valencia CF, ESP' })
103
+ 'Valencia' => 'Valencia CF, ESP',
104
+ 'Rangers FC' => 'Rangers FC, SCO',
105
+ })
99
106
  end
100
107
 
101
108
  # puts " [debug] auto_conf_teams:"
102
109
  # pp auto_conf_teams
103
110
 
104
111
 
105
- teams = catalog.teams.find_by!( name: auto_conf_teams.keys,
112
+ ## todo/fix
113
+ ## ** !!! ERROR - too many matches (2) for club >Barcelona<:
114
+ ## [<Club: FC Barcelona (ESP)>, <Club: Barcelona Guayaquil (ECU)>]
115
+
116
+ puts "league:"
117
+ pp league
118
+
119
+ teams = catalog.teams.find_by!( name: auto_conf_teams,
106
120
  league: league,
107
121
  mods: mods )
108
122
 
109
- # puts " [debug] teams:"
110
- # pp teams
123
+ puts " [debug] teams:"
124
+ pp teams
125
+
111
126
 
112
127
  ## build mapping - name => team struct record
113
- team_mapping = auto_conf_teams.keys.zip( teams ).to_h
128
+ team_mapping = auto_conf_teams.zip( teams ).to_h
114
129
 
115
- # puts " [debug] team_mapping:"
116
- # pp team_mapping
130
+ puts " [debug] team_mapping:"
131
+ pp team_mapping
117
132
 
118
-
119
133
 
120
- parser = MatchParser.new( lines,
121
- team_mapping,
122
- start ) ## note: keep season start_at date for now (no need for more specific stage date need for now)
134
+ ## quick (and dirty) hack
135
+ ## update all team strings with mapped records
136
+ matches.each do |match|
137
+ match.update( team1: team_mapping[ match.team1 ] )
138
+ match.update( team2: team_mapping[ match.team2 ] )
139
+ end
123
140
 
124
- matches, rounds, groups = parser.parse
141
+ ## quick hack cont.
142
+ ## rebuild groups with all team strings with mapped records
143
+ groups = groups.map do |old_group|
144
+ group = Import::Group.new(
145
+ name: old_group.name,
146
+ teams: old_group.teams.map {|team| team_mapping[team] }
147
+ )
148
+ group
149
+ end
150
+ puts "groups:"
151
+ pp groups
125
152
 
126
- pp rounds
127
- pp groups
153
+ ## fix
154
+ ## !! ERROR - no (cached) team rec found for team in group >Group A<
155
+ ## for >#<Sports::Club:0x000002c7e1686040><
156
+ ### update sync group to check for records (use .name) !!!
128
157
 
129
158
 
130
159
  ######################################################
@@ -166,10 +195,10 @@ class MatchReader ## todo/check: rename to MatchReaderV2 (use plural?) why? w
166
195
  ## e.g. group.teams assumes an array of team names e.g.
167
196
  ## ["Spain", "Czech Republic", "Turkey", "Croatia"]
168
197
  group_team_ids = []
169
- group.teams.each do |team_name|
170
- team_rec = cache_team_recs[ team_name ]
198
+ group.teams.each do |team|
199
+ team_rec = cache_team_recs[ team.name ]
171
200
  if team_rec.nil? ## assume team MUST always be present/known in mapping (via autoconfig parser)
172
- puts "!! ERROR - no (cached) team rec found for team in group >#{group.name}< for >#{team_name}<"
201
+ puts "!! ERROR - no (cached) team rec found for team in group >#{group.name}< for >#{team}<"
173
202
  exit 1
174
203
  end
175
204
  group_team_ids << team_rec.id
@@ -182,6 +211,7 @@ class MatchReader ## todo/check: rename to MatchReaderV2 (use plural?) why? w
182
211
  round_rec = Sync::Round.find_or_create( round, event: event_rec ) ## check: use/rename to EventRound why? why not?
183
212
  end
184
213
 
214
+
185
215
  matches.each do |match|
186
216
  ## note: pass along stage (if present): stage - optional from heading!!!!
187
217
  match = match.update( stage: stage ) if stage
@@ -193,10 +223,5 @@ class MatchReader ## todo/check: rename to MatchReaderV2 (use plural?) why? w
193
223
  end # method parse
194
224
 
195
225
 
196
- ######################
197
- # (convenience) helpers
198
-
199
- def catalog() Import.catalog; end
200
-
201
226
  end # class MatchReader
202
227
  end # module SportDb
@@ -0,0 +1,47 @@
1
+ module SportDb
2
+ module Sync
3
+ class Club
4
+
5
+ ## auto-cache all clubs by find_or_create for later mapping / lookup
6
+ def self.cache() @cache ||= {}; end
7
+
8
+
9
+ ##################################
10
+ # finders
11
+
12
+ def self.find_or_create( club )
13
+ ## note: assume "canonical unique" names for now for clubs
14
+ rec = Model::Team.find_by( name: club.name )
15
+ if rec.nil?
16
+
17
+ ## todo/fix: move auto-key gen to structs for re(use)!!!!!!
18
+ ## check if key is present otherwise generate e.g. remove all non-ascii a-z chars
19
+ key = club.key || club.name.downcase.gsub( /[^a-z]/, '' )
20
+ puts "add club: #{key}, #{club.name}, #{club.country.name} (#{club.country.key})"
21
+
22
+ attribs = {
23
+ key: key,
24
+ name: club.name,
25
+ country_id: Sync::Country.find_or_create( club.country ).id,
26
+ club: true,
27
+ national: false ## check -is default anyway - use - why? why not?
28
+ ## todo/fix: add city if present - why? why not?
29
+ }
30
+
31
+ attribs[:code] = club.code if club.code ## add code (abbreviation) if present
32
+
33
+ if club.alt_names.empty? == false
34
+ attribs[:alt_names] = club.alt_names.join('|')
35
+ end
36
+
37
+ rec = Model::Team.create!( attribs )
38
+ end
39
+ ## auto-add to cache
40
+ cache[club.name] = rec
41
+
42
+ rec
43
+ end
44
+
45
+ end # class Club
46
+ end # module Sync
47
+ end # module SportDb
@@ -0,0 +1,39 @@
1
+ module SportDb
2
+ module Sync
3
+ class Country
4
+
5
+ #############################
6
+ # finders
7
+
8
+ def self.find( country )
9
+ WorldDb::Model::Country.find_by( key: country.key )
10
+ end
11
+
12
+ def self.find!( country )
13
+ rec = find( country )
14
+ if rec.nil?
15
+ puts "** !!! ERROR !!! - country for key >#{country.key}< not found; sorry - add to COUNTRIES table"
16
+ exit 1
17
+ end
18
+ rec
19
+ end
20
+
21
+ def self.find_or_create( country )
22
+ rec = find( country )
23
+ if rec.nil?
24
+ attribs = {
25
+ key: country.key,
26
+ name: country.name,
27
+ code: country.code, ## fix: uses fifa code now (should be iso-alpha3 if available)
28
+ ## fifa: country.fifa,
29
+ area: 1,
30
+ pop: 1
31
+ }
32
+ rec = WorldDb::Model::Country.create!( attribs )
33
+ end
34
+ rec
35
+ end
36
+ end # class Country
37
+
38
+ end # module Sync
39
+ end # module SportDb
@@ -0,0 +1,72 @@
1
+ module SportDb
2
+ module Sync
3
+
4
+ class Event
5
+
6
+ ##################################################
7
+ # finders
8
+ def self.find_by( league:, season: )
9
+ ## note: allow passing in of activerecord db records too - why? why not?
10
+ ## fix - change ArgumentError to TypeError !! (if TypeError exists)
11
+ raise ArgumentError, "league struct record expected; got #{league.class.name}" unless league.is_a?( Import::League )
12
+ raise ArgumentError, "season struct record expected; got #{season.class.name}" unless season.is_a?( Import::Season )
13
+
14
+ ## auto-create league and season (db) records if missing? - why? why not?
15
+ season_rec = Season.find( season )
16
+ league_rec = League.find( league )
17
+
18
+ rec = nil
19
+ rec = Model::Event.find_by( league_id: league_rec.id,
20
+ season_id: season_rec.id ) if season_rec && league_rec
21
+ rec
22
+ end
23
+
24
+ def self.find_by!( league:, season: )
25
+ rec = find_by( league: league, season: season )
26
+ if rec.nil?
27
+ puts "** !!!ERROR!!! db sync - no event match found for:"
28
+ pp league
29
+ pp season
30
+ exit 1
31
+ end
32
+ rec
33
+ end
34
+
35
+ def self.find_or_create_by( league:, season: )
36
+ ## note: allow passing in of activerecord db records too - why? why not?
37
+ raise ArgumentError, "league struct record expected; got #{league.class.name}" unless league.is_a?( Import::League )
38
+ raise ArgumentError, "season struct record expected; got #{season.class.name}" unless season.is_a?( Import::Season )
39
+
40
+ ## note: auto-creates league and season (db) records if missing - why? why not?
41
+ season_rec = Season.find_or_create( season )
42
+ league_rec = League.find_or_create( league )
43
+
44
+ rec = Model::Event.find_by( league_id: league_rec.id,
45
+ season_id: season_rec.id )
46
+ if rec.nil?
47
+ attribs = {
48
+ league_id: league_rec.id,
49
+ season_id: season_rec.id,
50
+ }
51
+
52
+ ## quick hack/change later !!
53
+ ## todo/fix: check season - if is length 4 (single year) use 2017, 1, 1
54
+ ## otherwise use 2017, 7, 1
55
+ ## start_at use year and 7,1 e.g. Date.new( 2017, 7, 1 )
56
+ ## hack: fix/todo1!!
57
+ ## add "fake" start_date for now
58
+ attribs[:start_date] = if season.year? ## e.g. assume 2018 etc.
59
+ Date.new( season.start_year, 1, 1 )
60
+ else ## assume 2014/15 etc.
61
+ Date.new( season.start_year, 7, 1 )
62
+ end
63
+
64
+ rec = Model::Event.create!( attribs )
65
+ end
66
+ rec
67
+ end
68
+ end # class Event
69
+
70
+ end # module Sync
71
+ end # module SportDb
72
+
@@ -0,0 +1,40 @@
1
+ module SportDb
2
+ module Sync
3
+ class League
4
+
5
+ ###################################
6
+ # finders
7
+
8
+ def self.find( league )
9
+ Model::League.find_by( key: league.key )
10
+ end
11
+
12
+ def self.find!( league )
13
+ rec = find( league )
14
+ if rec.nil?
15
+ puts "** !!!ERROR!!! db sync - no league match found for:"
16
+ pp league
17
+ exit 1
18
+ end
19
+ rec
20
+ end
21
+
22
+ def self.find_or_create( league )
23
+ rec = find( league )
24
+ if rec.nil?
25
+ attribs = { key: league.key,
26
+ name: league.name }
27
+
28
+ if league.country
29
+ attribs[ :country_id ] = Sync::Country.find_or_create( league.country ).id
30
+ end
31
+
32
+ rec = Model::League.create!( attribs )
33
+ end
34
+ rec
35
+ end
36
+
37
+ end # class League
38
+ end # module Sync
39
+ end # module SportDb
40
+
@@ -0,0 +1,147 @@
1
+ module SportDb
2
+ module Sync
3
+
4
+ class Match
5
+ ### todo/fix: rename to create!! (add update support later) !!!!
6
+ ## use update_by_round or update_by_date or update_by_teams or such
7
+ ## NO easy (unique always auto-id match) possible!!!!!!
8
+ def self.create_or_update( match, event: )
9
+ ## note: MUST find round, thus, use bang (!)
10
+
11
+ ## todo/check: allow strings too - why? why not?
12
+
13
+
14
+ ## todo/check:
15
+ ## how to model "same" rounds in different stages
16
+ ## e.g. Belgium
17
+ ## in regular season (stage) - round 1, round 2, etc.
18
+ ## in playoff (stage) - round 1, round 2, etc.
19
+ ## reference same round or create a new one for each stage!!???
20
+ ## and lookup by name AND stage??
21
+
22
+
23
+ round_rec = if match.round
24
+ ## query for round - allow string or round rec
25
+ round_name = match.round.is_a?( String ) ? match.round : match.round.name
26
+ Model::Round.find_by!( event_id: event.id,
27
+ name: round_name )
28
+ else # note: allow matches WITHOUT rounds too (e.g. England Football League 1888 and others)
29
+ nil
30
+ end
31
+
32
+ ## todo/check: allow fallback with db lookup if NOT found in cache - why? why not?
33
+ ## or better use Sync::Team.find_or_create( team ) !!!!!!! to auto-create on first hit!
34
+ ## || Team.find_or_create( team1 ) -- note: does NOT work for string (only recs) - what to do?
35
+ ## || Model::Team.find_by!( name: team1_name )
36
+ team1_name = match.team1.is_a?( String ) ? match.team1 : match.team1.name
37
+ team1_rec = Team.cache[ team1_name ]
38
+ team2_name = match.team2.is_a?( String ) ? match.team2 : match.team2.name
39
+ team2_rec = Team.cache[ team2_name ]
40
+
41
+ ## check optional group (e.g. Group A, etc.)
42
+ group_rec = if match.group
43
+ group_name = match.group.is_a?( String ) ? match.group : match.group.name
44
+ Model::Group.find_by!( event_id: event.id,
45
+ name: group_name )
46
+ else
47
+ nil
48
+ end
49
+
50
+ ## check optional stage (e.g. Regular, Play Off, Relegation, etc. )
51
+ stage_rec = if match.stage
52
+ stage_name = match.stage.is_a?( String ) ? match.stage : match.stage.name
53
+ Model::Stage.find_by!( event_id: event.id,
54
+ name: stage_name )
55
+ else
56
+ nil
57
+ end
58
+
59
+ ### todo/check: what happens if there's more than one match? exception raised??
60
+ rec = if ['N. N.'].include?( team1_name ) && ## some special cases - always assume new record for now (to avoid ambigious update conflict)
61
+ ['N. N.'].include?( team2_name )
62
+ ## always assume new record for now
63
+ ## check for date or such - why? why not?
64
+ nil
65
+ elsif round_rec
66
+ ## add match status too? allows [abandoned] and [replay] in same round
67
+ find_attributes = { round_id: round_rec.id,
68
+ team1_id: team1_rec.id,
69
+ team2_id: team2_rec.id }
70
+
71
+ ## add stage if present to query
72
+ find_attributes[ :stage_id] = stage_rec.id if stage_rec
73
+
74
+ Model::Match.find_by( find_attributes )
75
+ else
76
+ ## always assume new record for now
77
+ ## check for date or such - why? why not?
78
+ nil
79
+ end
80
+
81
+ if rec.nil?
82
+ ## find last pos - check if it can be nil? yes, is nil if no records found
83
+ max_pos = Model::Match.where( event_id: event.id ).maximum( 'pos' )
84
+ max_pos = max_pos ? max_pos+1 : 1
85
+
86
+ attribs = { event_id: event.id, ## todo/fix: change to data struct too?
87
+ team1_id: team1_rec.id,
88
+ team2_id: team2_rec.id,
89
+ pos: max_pos,
90
+ num: match.num, ## note - might be nil (not nil for euro, world cup, etc.)
91
+ # date: match.date.to_date, ## todo/fix: split and add date & time!!!!
92
+ date: match.date, # assume iso format as string e.g. 2021-07-10 !!!
93
+ time: match.time, # assume iso format as string e.g. 21:00 !!!
94
+ score1: match.score1,
95
+ score2: match.score2,
96
+ score1i: match.score1i,
97
+ score2i: match.score2i,
98
+ score1et: match.score1et,
99
+ score2et: match.score2et,
100
+ score1p: match.score1p,
101
+ score2p: match.score2p,
102
+ status: match.status }
103
+
104
+ attribs[ :round_id ] = round_rec.id if round_rec
105
+ attribs[ :group_id ] = group_rec.id if group_rec
106
+ attribs[ :stage_id ] = stage_rec.id if stage_rec
107
+
108
+ rec = Model::Match.create!( attribs )
109
+
110
+
111
+ #############################################
112
+ ### try to update goals
113
+ #### quick & dirty v0 - redo !!!!
114
+ goals = match.goals
115
+ if goals && goals.size > 0
116
+ goals.each do |goal|
117
+ person_rec = Model::Person.find_by(
118
+ name: goal.player )
119
+ if person_rec.nil?
120
+ person_rec = Model::Person.create!(
121
+ key: goal.player.downcase.gsub( /[^a-z]/, '' ),
122
+ name: goal.player
123
+ )
124
+ end
125
+ Model::Goal.create!(
126
+ match_id: rec.id,
127
+ person_id: person_rec.id,
128
+ team_id: goal.team == 1 ? rec.team1.id
129
+ : rec.team2.id,
130
+ minute: goal.minute,
131
+ offset: goal.offset || 0,
132
+ penalty: goal.penalty,
133
+ owngoal: goal.owngoal,
134
+ )
135
+ end
136
+ end
137
+ else
138
+ # update - todo
139
+ puts "!! ERROR - match updates not yet supported (only inserts); sorry"
140
+ exit 1
141
+ end
142
+ rec
143
+ end
144
+ end # class Match
145
+
146
+ end # module Sync
147
+ end # module SportDb
@@ -0,0 +1,136 @@
1
+
2
+ module SportDb
3
+ module Sync
4
+
5
+
6
+ class NationalTeam
7
+ def self.find_or_create( team )
8
+ rec = Model::Team.find_by( name: team.name )
9
+ if rec.nil?
10
+ puts "add national team: #{team.key}, #{team.name}, #{team.country.name} (#{team.country.key})"
11
+
12
+ ### note: key expected three or more lowercase letters a-z /\A[a-z]{3,}\z/
13
+ attribs = {
14
+ key: team.key, ## note: always use downcase fifa code for now!!!
15
+ name: team.name,
16
+ code: team.code,
17
+ country_id: Sync::Country.find_or_create( team.country ).id,
18
+ club: false,
19
+ national: true ## check -is default anyway - use - why? why not?
20
+ }
21
+
22
+ if team.alt_names.empty? == false
23
+ attribs[:alt_names] = team.alt_names.join('|')
24
+ end
25
+
26
+ rec = Model::Team.create!( attribs )
27
+ end
28
+ rec
29
+ end
30
+ end # class NationalTeam
31
+
32
+
33
+ class Team
34
+ ## auto-cache all clubs by find_or_create for later mapping / lookup
35
+ def self.cache() @cache ||= {}; end
36
+
37
+ def self.find_or_create( team_or_teams )
38
+ if team_or_teams.is_a?( Array )
39
+ recs = []
40
+ teams = team_or_teams
41
+ teams.each do |team|
42
+ recs << __find_or_create( team )
43
+ end
44
+ recs
45
+ else # assome single rec
46
+ team = team_or_teams
47
+ __find_or_create( team )
48
+ end
49
+ end
50
+
51
+ def self.__find_or_create( team ) ## todo/check: use find_or_create_worker instead of _find - why? why not?
52
+ rec = if team.is_a?( Import::NationalTeam )
53
+ NationalTeam.find_or_create( team )
54
+ else ## assume Club
55
+ Club.find_or_create( team )
56
+ end
57
+ cache[ team.name ] = rec ## note: assume "canonical" unique team name
58
+ rec
59
+ end
60
+ end # class Team
61
+
62
+
63
+
64
+ class Round
65
+ def self.find_or_create( round, event: )
66
+ rec = Model::Round.find_by( name: round.name, event_id: event.id )
67
+ if rec.nil?
68
+ ## find last pos - check if it can be nil?
69
+ max_pos = Model::Round.where( event_id: event.id ).maximum( 'pos' )
70
+ max_pos = max_pos ? max_pos+1 : 1
71
+
72
+ attribs = { event_id: event.id,
73
+ name: round.name,
74
+ pos: max_pos
75
+ }
76
+
77
+ ## todo/fix: check if round has (optional) start or end date and add!!!
78
+ ## attribs[ :start_date] = round.start_date.to_date
79
+
80
+ rec = Model::Round.create!( attribs )
81
+ end
82
+ rec
83
+ end
84
+ end # class Round
85
+
86
+
87
+ class Group
88
+ def self.find_or_create( group, event: )
89
+ rec = Model::Group.find_by( name: group.name, event_id: event.id )
90
+ if rec.nil?
91
+ ## find last pos - check if it can be nil?
92
+ max_pos = Model::Group.where( event_id: event.id ).maximum( 'pos' )
93
+ max_pos = max_pos ? max_pos+1 : 1
94
+
95
+ attribs = { event_id: event.id,
96
+ name: group.name,
97
+ pos: max_pos
98
+ }
99
+
100
+ ## todo/fix: check/add optional group key (was: pos before)!!!!
101
+ rec = Model::Group.create!( attribs )
102
+ end
103
+ ## todo/fix: add/update teams in group too!!!!!
104
+ rec
105
+ end
106
+ end # class Group
107
+
108
+
109
+ class Stage
110
+ def self.find( name, event: )
111
+ Model::Stage.find_by( name: name, event_id: event.id )
112
+ end
113
+ def self.find!( name, event: )
114
+ rec = find( name, event: event )
115
+ if rec.nil?
116
+ puts "** !!!ERROR!!! db sync - no stage match found for:"
117
+ pp name
118
+ pp event
119
+ exit 1
120
+ end
121
+ rec
122
+ end
123
+
124
+ def self.find_or_create( name, event: )
125
+ rec = find( name, event: event )
126
+ if rec.nil?
127
+ attribs = { event_id: event.id,
128
+ name: name,
129
+ }
130
+ rec = Model::Stage.create!( attribs )
131
+ end
132
+ rec
133
+ end
134
+ end # class Stage
135
+ end # module Sync
136
+ end # module SportDb
@@ -0,0 +1,36 @@
1
+ module SportDb
2
+ module Sync
3
+ class Season
4
+
5
+ ################
6
+ # finders
7
+
8
+ def self.find( season )
9
+ season = Season( season ) ## auto-convert for now (for old compat) - why? why not?
10
+ Model::Season.find_by( key: season.key )
11
+ end
12
+
13
+ def self.find!( season )
14
+ season = Season( season ) ## auto-convert for now (for old compat) - why? why not?
15
+ rec = find( season )
16
+ if rec.nil?
17
+ puts "** !!!ERROR!!! db sync - no season match found for >#{season.key}<:"
18
+ exit 1
19
+ end
20
+ rec
21
+ end
22
+
23
+ def self.find_or_create( season )
24
+ season = Season( season ) ## auto-convert for now (for old compat) - why? why not?
25
+ rec = find( season )
26
+ if rec.nil?
27
+ attribs = { key: season.key,
28
+ name: season.name }
29
+ rec = Model::Season.create!( attribs )
30
+ end
31
+ rec
32
+ end
33
+ end # class Season
34
+ end # module Sync
35
+ end # module SportDb
36
+
@@ -1,10 +1,11 @@
1
1
 
2
2
 
3
3
  module SportDb
4
+ module Module
4
5
  module Readers
5
6
 
6
- MAJOR = 1 ## todo: namespace inside version or something - why? why not??
7
- MINOR = 2
7
+ MAJOR = 2 ## todo: namespace inside version or something - why? why not??
8
+ MINOR = 0
8
9
  PATCH = 0
9
10
  VERSION = [MAJOR,MINOR,PATCH].join('.')
10
11
 
@@ -21,4 +22,5 @@ module Readers
21
22
  end
22
23
 
23
24
  end # module Readers
25
+ end # module Module
24
26
  end # module SportDb
@@ -1,10 +1,22 @@
1
-
2
- require 'sportdb/sync'
1
+ require 'sportdb/formats'
2
+ require 'sportdb/catalogs'
3
+ require 'sportdb/models'
3
4
 
4
5
 
5
6
  ###
6
7
  # our own code
7
8
  require_relative 'readers/version' # let version always go first
9
+
10
+ ## add sync stuff
11
+ require_relative 'readers/sync/country'
12
+ require_relative 'readers/sync/league'
13
+ require_relative 'readers/sync/season'
14
+ require_relative 'readers/sync/event'
15
+ require_relative 'readers/sync/club'
16
+ require_relative 'readers/sync/more'
17
+ require_relative 'readers/sync/match' ## note - let match sync go last
18
+
19
+ ## add readers & friends
8
20
  require_relative 'readers/conf_reader'
9
21
  require_relative 'readers/match_reader'
10
22
  require_relative 'readers/package'
@@ -12,7 +24,6 @@ require_relative 'readers/package'
12
24
 
13
25
 
14
26
 
15
-
16
27
  ##
17
28
  ## add convenience shortcut helpers
18
29
  module SportDb
@@ -57,4 +68,4 @@ end # module SportDb
57
68
 
58
69
 
59
70
 
60
- puts SportDb::Readers.banner # say hello
71
+ puts SportDb::Module::Readers.banner # say hello
metadata CHANGED
@@ -1,29 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sportdb-readers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.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-06-23 00:00:00.000000000 Z
11
+ date: 2024-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: sportdb-sync
14
+ name: sportdb-formats
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.2.0
19
+ version: 2.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.2.0
26
+ version: 2.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: sportdb-catalogs
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.2.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: sportdb-models
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.1.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.1.0
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rdoc
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -78,6 +106,13 @@ files:
78
106
  - lib/sportdb/readers/conf_reader.rb
79
107
  - lib/sportdb/readers/match_reader.rb
80
108
  - lib/sportdb/readers/package.rb
109
+ - lib/sportdb/readers/sync/club.rb
110
+ - lib/sportdb/readers/sync/country.rb
111
+ - lib/sportdb/readers/sync/event.rb
112
+ - lib/sportdb/readers/sync/league.rb
113
+ - lib/sportdb/readers/sync/match.rb
114
+ - lib/sportdb/readers/sync/more.rb
115
+ - lib/sportdb/readers/sync/season.rb
81
116
  - lib/sportdb/readers/version.rb
82
117
  homepage: https://github.com/sportdb/sport.db
83
118
  licenses:
@@ -93,7 +128,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
93
128
  requirements:
94
129
  - - ">="
95
130
  - !ruby/object:Gem::Version
96
- version: 2.2.2
131
+ version: 3.1.0
97
132
  required_rubygems_version: !ruby/object:Gem::Requirement
98
133
  requirements:
99
134
  - - ">="