sportdb-readers 0.0.1
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 +7 -0
- data/CHANGELOG.md +3 -0
- data/Manifest.txt +14 -0
- data/README.md +26 -0
- data/Rakefile +31 -0
- data/lib/sportdb/readers.rb +22 -0
- data/lib/sportdb/readers/event_reader.rb +104 -0
- data/lib/sportdb/readers/match_parser.rb +466 -0
- data/lib/sportdb/readers/match_reader.rb +952 -0
- data/lib/sportdb/readers/outline_reader.rb +83 -0
- data/lib/sportdb/readers/sync.rb +208 -0
- data/lib/sportdb/readers/version.rb +25 -0
- data/test/helper.rb +21 -0
- data/test/test_match_parser.rb +101 -0
- data/test/test_reader.rb +35 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f26c8211ce5ab383f35775134f230cbc22d71112
|
4
|
+
data.tar.gz: 7bfb83d1e338e101578fdd4487e3fa35c7f13580
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cee60d57c7f687397f0daa0333cdd6eaeee924141215b57ecc89691d3ae6daf5b40bf6f4aca4d65cf713613732e2ccafd7c9cd0256564b58e3d516241b495a14
|
7
|
+
data.tar.gz: fc4dcbc250a5d56b31bdbe6f82b78073e45ec1df499554861cc623695deb752ce94fe1934fe4b69b47b2245e44a6baeafb07b2eb9de0f2134317cac1eccb15d6
|
data/CHANGELOG.md
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
CHANGELOG.md
|
2
|
+
Manifest.txt
|
3
|
+
README.md
|
4
|
+
Rakefile
|
5
|
+
lib/sportdb/readers.rb
|
6
|
+
lib/sportdb/readers/event_reader.rb
|
7
|
+
lib/sportdb/readers/match_parser.rb
|
8
|
+
lib/sportdb/readers/match_reader.rb
|
9
|
+
lib/sportdb/readers/outline_reader.rb
|
10
|
+
lib/sportdb/readers/sync.rb
|
11
|
+
lib/sportdb/readers/version.rb
|
12
|
+
test/helper.rb
|
13
|
+
test/test_match_parser.rb
|
14
|
+
test/test_reader.rb
|
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# sportdb-readers - sport.db readers
|
2
|
+
|
3
|
+
|
4
|
+
* home :: [github.com/sportdb/sport.db](https://github.com/sportdb/sport.db)
|
5
|
+
* bugs :: [github.com/sportdb/sport.db/issues](https://github.com/sportdb/sport.db/issues)
|
6
|
+
* gem :: [rubygems.org/gems/sportdb-readers](https://rubygems.org/gems/sportdb-readers)
|
7
|
+
* rdoc :: [rubydoc.info/gems/sportdb-readers](http://rubydoc.info/gems/sportdb-readers)
|
8
|
+
* forum :: [opensport](http://groups.google.com/group/opensport)
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
To be done
|
15
|
+
|
16
|
+
## License
|
17
|
+
|
18
|
+
The `sportdb-readers` scripts are dedicated to the public domain.
|
19
|
+
Use it as you please with no restrictions whatsoever.
|
20
|
+
|
21
|
+
|
22
|
+
## Questions? Comments?
|
23
|
+
|
24
|
+
Send them along to the
|
25
|
+
[Open Sports & Friends Forum/Mailing List](http://groups.google.com/group/opensport).
|
26
|
+
Thanks!
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/sportdb/readers/version.rb'
|
3
|
+
|
4
|
+
Hoe.spec 'sportdb-readers' do
|
5
|
+
|
6
|
+
self.version = SportDb::Readers::VERSION
|
7
|
+
|
8
|
+
self.summary = "sportdb-readers - sport.db readers"
|
9
|
+
self.description = summary
|
10
|
+
|
11
|
+
self.urls = ['https://github.com/sportdb/sport.db']
|
12
|
+
|
13
|
+
self.author = 'Gerald Bauer'
|
14
|
+
self.email = 'opensport@googlegroups.com'
|
15
|
+
|
16
|
+
# switch extension to .markdown for gihub formatting
|
17
|
+
self.readme_file = 'README.md'
|
18
|
+
self.history_file = 'CHANGELOG.md'
|
19
|
+
|
20
|
+
self.licenses = ['Public Domain']
|
21
|
+
|
22
|
+
self.extra_deps = [
|
23
|
+
['sportdb-config', '>= 0.8.1'],
|
24
|
+
['sportdb-models', '>= 1.18.0'],
|
25
|
+
]
|
26
|
+
|
27
|
+
self.spec_extras = {
|
28
|
+
required_ruby_version: '>= 2.2.2'
|
29
|
+
}
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
|
4
|
+
require 'sportdb/config'
|
5
|
+
require 'sportdb/models' ## add sql database support
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
###
|
10
|
+
# our own code
|
11
|
+
require 'sportdb/readers/version' # let version always go first
|
12
|
+
require 'sportdb/readers/sync'
|
13
|
+
require 'sportdb/readers/outline_reader'
|
14
|
+
require 'sportdb/readers/event_reader'
|
15
|
+
require 'sportdb/readers/match_parser'
|
16
|
+
require 'sportdb/readers/match_reader'
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
puts SportDb::Readers.banner # say hello
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SportDb
|
4
|
+
|
5
|
+
|
6
|
+
class EventReaderV2 ## todo/check: rename to EventsReaderV2 (use plural?) why? why not?
|
7
|
+
|
8
|
+
def self.read( path ) ## use - rename to read_file or from_file etc. - why? why not?
|
9
|
+
txt = File.open( path, 'r:utf-8' ).read
|
10
|
+
parse( txt )
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.parse( txt )
|
14
|
+
recs = LeagueOutlineReader.parse( txt )
|
15
|
+
pp recs
|
16
|
+
|
17
|
+
## pass 2 - check & map; replace inline (string with record)
|
18
|
+
recs.each do |rec|
|
19
|
+
league = rec[:league]
|
20
|
+
clubs = [] ## convert lines to clubs
|
21
|
+
rec[:lines].each do |line|
|
22
|
+
|
23
|
+
next if line =~ /^[ -]+$/ ## skip decorative lines with dash only (e.g. ---- or - - - -) etc.
|
24
|
+
|
25
|
+
scan = StringScanner.new( line )
|
26
|
+
|
27
|
+
if scan.check( /\d{1,2}[ ]+/ ) ## entry with standaning starts with ranking e.g. 1,2,3, etc.
|
28
|
+
puts " table entry >#{line}<"
|
29
|
+
rank = scan.scan( /\d{1,2}[ ]+/ ).strip # note: strip trailing spaces
|
30
|
+
|
31
|
+
## note: uses look ahead scan until we hit at least two spaces
|
32
|
+
## or the end of string (standing records for now optional)
|
33
|
+
name = scan.scan_until( /(?=\s{2})|$/ )
|
34
|
+
if scan.eos?
|
35
|
+
standing = nil
|
36
|
+
else
|
37
|
+
standing = scan.rest.strip # note: strip leading and trailing spaces
|
38
|
+
end
|
39
|
+
puts " rank: >#{rank}<, name: >#{name}<, standing: >#{standing}<"
|
40
|
+
|
41
|
+
## note: rank and standing gets ignored (not used) for now
|
42
|
+
else
|
43
|
+
## assume club is full line
|
44
|
+
name = line
|
45
|
+
end
|
46
|
+
|
47
|
+
clubs << find_club( name, league.country )
|
48
|
+
end
|
49
|
+
|
50
|
+
rec[:clubs] = clubs
|
51
|
+
rec.delete( :lines ) ## remove lines entry
|
52
|
+
end
|
53
|
+
|
54
|
+
## pass 3 - import (insert/update) into db
|
55
|
+
recs.each do |rec|
|
56
|
+
league = Sync::League.find_or_create( rec[:league] )
|
57
|
+
season = Sync::Season.find_or_create( rec[:season] )
|
58
|
+
|
59
|
+
event = Sync::Event.find_or_create( league: league, season: season )
|
60
|
+
|
61
|
+
rec[:clubs].each do |club_rec|
|
62
|
+
club = Sync::Club.find_or_create( club_rec )
|
63
|
+
## add teams to event
|
64
|
+
## todo/fix: check if team is alreay included?
|
65
|
+
## or clear/destroy_all first!!!
|
66
|
+
event.teams << club
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
recs
|
71
|
+
end # method read
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
def self.find_club( name, country ) ## todo/fix: add international or league flag?
|
76
|
+
club = nil
|
77
|
+
m = CLUBS.match_by( name: name, country: country )
|
78
|
+
|
79
|
+
if m.nil?
|
80
|
+
## (re)try with second country - quick hacks for known leagues
|
81
|
+
## todo/fix: add league flag to activate!!!
|
82
|
+
m = CLUBS.match_by( name: name, country: COUNTRIES['wal']) if country.key == 'eng'
|
83
|
+
m = CLUBS.match_by( name: name, country: COUNTRIES['nir']) if country.key == 'ie'
|
84
|
+
m = CLUBS.match_by( name: name, country: COUNTRIES['mc']) if country.key == 'fr'
|
85
|
+
m = CLUBS.match_by( name: name, country: COUNTRIES['li']) if country.key == 'ch'
|
86
|
+
m = CLUBS.match_by( name: name, country: COUNTRIES['ca']) if country.key == 'us'
|
87
|
+
end
|
88
|
+
|
89
|
+
if m.nil?
|
90
|
+
puts "** !!! ERROR !!! no match for club >#{name}<"
|
91
|
+
exit 1
|
92
|
+
elsif m.size > 1
|
93
|
+
puts "** !!! ERROR !!! too many matches (#{m.size}) for club >#{name}<:"
|
94
|
+
pp m
|
95
|
+
exit 1
|
96
|
+
else # bingo; match - assume size == 1
|
97
|
+
club = m[0]
|
98
|
+
end
|
99
|
+
|
100
|
+
club
|
101
|
+
end
|
102
|
+
|
103
|
+
end # class EventReaderV2
|
104
|
+
end # module SportDb
|
@@ -0,0 +1,466 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SportDb
|
4
|
+
|
5
|
+
class MatchParserSimpleV2 ## simple match parser for club match schedules
|
6
|
+
include LogUtils::Logging
|
7
|
+
|
8
|
+
def initialize( lines, teams, start_at )
|
9
|
+
@lines = lines ## todo/check: change to text instead of array of lines - why? why not?
|
10
|
+
@mapper_teams = TeamMapper.new( teams )
|
11
|
+
@start_at = start_at
|
12
|
+
|
13
|
+
## build lookup hash by (team) key
|
14
|
+
@teams = teams.reduce({}) { |h,team| h[team.key]=team; h }
|
15
|
+
|
16
|
+
## debug_dump_teams( teams )
|
17
|
+
## exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def debug_dump_teams( teams )
|
21
|
+
puts "== #{teams.size} teams"
|
22
|
+
teams.each do |team|
|
23
|
+
print "#{team.key}, "
|
24
|
+
print "#{team.title}, "
|
25
|
+
print "#{team.synonyms.split('|').join(', ')}"
|
26
|
+
puts
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
Round = Struct.new( :pos, :title )
|
33
|
+
##
|
34
|
+
## todo: change db schema
|
35
|
+
## make start and end date optional
|
36
|
+
## change pos to num - why? why not?
|
37
|
+
## make pos/num optional too
|
38
|
+
##
|
39
|
+
## sort round by scheduled/planed start date
|
40
|
+
Match = Struct.new( :date,
|
41
|
+
:team1, :team2, ## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
|
42
|
+
:score1i, :score2i, ## half time (first (i) part)
|
43
|
+
:score1, :score2, ## full time
|
44
|
+
:round )
|
45
|
+
|
46
|
+
def parse
|
47
|
+
@last_date = nil
|
48
|
+
@last_round = nil
|
49
|
+
@rounds = {}
|
50
|
+
@matches = []
|
51
|
+
|
52
|
+
|
53
|
+
@lines.each do |line|
|
54
|
+
if is_round?( line )
|
55
|
+
parse_round_header( line )
|
56
|
+
elsif try_parse_game( line )
|
57
|
+
# do nothing here
|
58
|
+
elsif try_parse_date_header( line )
|
59
|
+
# do nothing here
|
60
|
+
else
|
61
|
+
logger.info "skipping line (no match found): >#{line}<"
|
62
|
+
end
|
63
|
+
end # lines.each
|
64
|
+
|
65
|
+
[@rounds.values, @matches]
|
66
|
+
end # method parse
|
67
|
+
|
68
|
+
|
69
|
+
def is_round?( line )
|
70
|
+
## note: =~ return nil if not match found, and 0,1, etc for match
|
71
|
+
(line =~ SportDb.lang.regex_round) != nil
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def find_round_pos!( line )
|
76
|
+
# pass #1) extract optional round pos from line
|
77
|
+
# e.g. (1) - must start line
|
78
|
+
regex_pos = /^[ \t]*\((\d{1,3})\)[ \t]+/
|
79
|
+
|
80
|
+
# pass #2) find free standing number e.g. Matchday 3 or Round 5 or 3. Spieltag etc.
|
81
|
+
# note: /\b(\d{1,3})\b/
|
82
|
+
# will match -12
|
83
|
+
# thus, use space required - will NOT match -2 e.g. Group-2 Play-off
|
84
|
+
# note: allow 1. Runde n
|
85
|
+
# 1^ Giornata
|
86
|
+
regex_num = /(?:^|\s)(\d{1,3})(?:[.\^\s]|$)/
|
87
|
+
|
88
|
+
if line =~ regex_pos
|
89
|
+
logger.debug " pos: >#{$1}<"
|
90
|
+
|
91
|
+
line.sub!( regex_pos, '[ROUND.POS] ' ) ## NB: add back trailing space that got swallowed w/ regex -> [ \t]+
|
92
|
+
return $1.to_i
|
93
|
+
elsif line =~ regex_num
|
94
|
+
## assume number in title is pos (e.g. Jornada 3, 3 Runde etc.)
|
95
|
+
## NB: do NOT remove pos from string (will get removed by round title)
|
96
|
+
|
97
|
+
num = $1.to_i # note: clone capture; keep a copy (another regex follows; will redefine $1)
|
98
|
+
|
99
|
+
#### fix:
|
100
|
+
# use/make keywords required
|
101
|
+
# e.g. Round of 16 -> should NOT match 16!
|
102
|
+
# Spiel um Platz 3 (or 5) etc -> should NOT match 3!
|
103
|
+
# Round 16 - ok
|
104
|
+
# thus, check for required keywords
|
105
|
+
|
106
|
+
## quick hack for round of 16
|
107
|
+
# todo: mask match e.g. Round of xxx ... and try again - might include something
|
108
|
+
# reuse pattern for Group XX Replays for example
|
109
|
+
if line =~ /^\s*Round of \d{1,3}\b/
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
logger.debug " pos: >#{num}<"
|
114
|
+
return num
|
115
|
+
else
|
116
|
+
## fix: add logger.warn no round pos found in line
|
117
|
+
return nil
|
118
|
+
end
|
119
|
+
end # method find_round_pos!
|
120
|
+
|
121
|
+
def find_round_header_title!( line )
|
122
|
+
# assume everything left is the round title
|
123
|
+
# extract all other items first (round title2, round pos, group title n pos, etc.)
|
124
|
+
|
125
|
+
## todo/fix:
|
126
|
+
## cleanup method
|
127
|
+
## use buf.index( '//' ) to split string (see found_round_def)
|
128
|
+
## why? simpler why not?
|
129
|
+
## - do we currently allow groups if title2 present? add example if it works?
|
130
|
+
|
131
|
+
buf = line.dup
|
132
|
+
logger.debug " find_round_header_title! line-before: >>#{buf}<<"
|
133
|
+
|
134
|
+
buf.gsub!( /\[[^\]]+\]/, '' ) # e.g. remove [ROUND.POS], [ROUND.TITLE2], [GROUP.TITLE+POS] etc.
|
135
|
+
buf.strip! # remove leading and trailing whitespace
|
136
|
+
|
137
|
+
logger.debug " find_round_title! line-after: >>#{buf}<<"
|
138
|
+
|
139
|
+
### bingo - assume what's left is the round title
|
140
|
+
|
141
|
+
logger.debug " title: >>#{buf}<<"
|
142
|
+
line.sub!( buf, '[ROUND.TITLE]' )
|
143
|
+
|
144
|
+
buf
|
145
|
+
end
|
146
|
+
|
147
|
+
def parse_round_header( line )
|
148
|
+
logger.debug "parsing round header line: >#{line}<"
|
149
|
+
|
150
|
+
## todo/check/fix:
|
151
|
+
# make sure Round of 16 will not return pos 16 -- how? possible?
|
152
|
+
# add unit test too to verify
|
153
|
+
pos = find_round_pos!( line )
|
154
|
+
|
155
|
+
title = find_round_header_title!( line )
|
156
|
+
|
157
|
+
logger.debug " line: >#{line}<"
|
158
|
+
|
159
|
+
|
160
|
+
round = @rounds[ title ]
|
161
|
+
if round.nil?
|
162
|
+
round = Round.new( pos, title )
|
163
|
+
@rounds[ title ] = round
|
164
|
+
end
|
165
|
+
## todo/check: if pos match (MUST always match for now)
|
166
|
+
@last_round = round
|
167
|
+
|
168
|
+
|
169
|
+
## NB: dummy/placeholder start_at, end_at date
|
170
|
+
## replace/patch after adding all games for round
|
171
|
+
|
172
|
+
=begin
|
173
|
+
round_attribs = {
|
174
|
+
title: title,
|
175
|
+
title2: title2,
|
176
|
+
knockout: knockout_flag
|
177
|
+
}
|
178
|
+
|
179
|
+
if pos > 999000
|
180
|
+
# no pos (e.g. will get autonumbered later) - try match by title for now
|
181
|
+
# e.g. lets us use title 'Group Replays', for example, multiple times
|
182
|
+
@round = Round.find_by_event_id_and_title( @event.id, title )
|
183
|
+
else
|
184
|
+
@round = Round.find_by_event_id_and_pos( @event.id, pos )
|
185
|
+
end
|
186
|
+
|
187
|
+
if @round.present?
|
188
|
+
logger.debug "update round #{@round.id}:"
|
189
|
+
else
|
190
|
+
logger.debug "create round:"
|
191
|
+
@round = Round.new
|
192
|
+
|
193
|
+
round_attribs = round_attribs.merge( {
|
194
|
+
event_id: @event.id,
|
195
|
+
pos: pos,
|
196
|
+
start_at: Date.parse('1911-11-11'),
|
197
|
+
end_at: Date.parse('1911-11-11')
|
198
|
+
})
|
199
|
+
end
|
200
|
+
|
201
|
+
logger.debug round_attribs.to_json
|
202
|
+
|
203
|
+
@round.update_attributes!( round_attribs )
|
204
|
+
|
205
|
+
@patch_round_ids_pos << @round.id if pos > 999000
|
206
|
+
### store list of round ids for patching start_at/end_at at the end
|
207
|
+
@patch_round_ids_dates << @round.id # todo/fix/check: check if round has definition (do NOT patch if definition (not auto-added) present)
|
208
|
+
=end
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
def find_scores!( line, opts={} )
|
213
|
+
# note: always call after find_dates !!!
|
214
|
+
# scores match date-like patterns!! e.g. 10-11 or 10:00 etc.
|
215
|
+
# -- note: score might have two digits too
|
216
|
+
|
217
|
+
finder = ScoresFinder.new
|
218
|
+
finder.find!( line, opts )
|
219
|
+
end
|
220
|
+
|
221
|
+
def try_parse_game( line )
|
222
|
+
# note: clone line; for possible test do NOT modify in place for now
|
223
|
+
# note: returns true if parsed, false if no match
|
224
|
+
parse_game( line.dup )
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
def parse_game( line )
|
229
|
+
logger.debug "parsing game (fixture) line: >#{line}<"
|
230
|
+
|
231
|
+
@mapper_teams.map_teams!( line ) ### todo/fix: limit mapping to two(2) teams - why? why not? might avoid matching @ Barcelona ??
|
232
|
+
team_keys = @mapper_teams.find_teams!( line )
|
233
|
+
team1_key = team_keys[0]
|
234
|
+
team2_key = team_keys[1]
|
235
|
+
|
236
|
+
## note: if we do NOT find two teams; return false - no match found
|
237
|
+
if team1_key.nil? || team2_key.nil?
|
238
|
+
logger.debug " no game match (two teams required) found for line: >#{line}<"
|
239
|
+
return false
|
240
|
+
end
|
241
|
+
|
242
|
+
## pos = find_game_pos!( line )
|
243
|
+
|
244
|
+
date = find_date!( line, start_at: @start_at )
|
245
|
+
|
246
|
+
###
|
247
|
+
# check if date found?
|
248
|
+
# NB: ruby falsey is nil & false only (not 0 or empty array etc.)
|
249
|
+
if date
|
250
|
+
### check: use date_v2 if present? why? why not?
|
251
|
+
@last_date = date # keep a reference for later use
|
252
|
+
else
|
253
|
+
date = @last_date # no date found; (re)use last seen date
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
scores = find_scores!( line )
|
258
|
+
|
259
|
+
logger.debug " line: >#{line}<"
|
260
|
+
|
261
|
+
|
262
|
+
## todo/check: scores are integers or strings?
|
263
|
+
@matches << Match.new( date,
|
264
|
+
@teams[ team1_key ],
|
265
|
+
@teams[ team2_key ],
|
266
|
+
scores[0], ## score1i - half time (first (i) part)
|
267
|
+
scores[1], ## score2i
|
268
|
+
scores[2], ## score1 - full time
|
269
|
+
scores[3], ## score2
|
270
|
+
@last_round )
|
271
|
+
|
272
|
+
### todo: cache team lookups in hash?
|
273
|
+
|
274
|
+
=begin
|
275
|
+
team1 = Team.find_by_key!( team1_key )
|
276
|
+
team2 = Team.find_by_key!( team2_key )
|
277
|
+
|
278
|
+
@last_team1 = team1 # store for later use for goals etc.
|
279
|
+
@last_team2 = team2
|
280
|
+
|
281
|
+
|
282
|
+
if @round.nil?
|
283
|
+
## no round header found; calculate round from date
|
284
|
+
|
285
|
+
###
|
286
|
+
## todo/fix: add some unit tests for round look up
|
287
|
+
# fix: use date_v2 if present!! (old/original date; otherwise use date)
|
288
|
+
|
289
|
+
#
|
290
|
+
# fix: check - what to do with hours e.g. start_at use 00:00 and for end_at use 23.59 ??
|
291
|
+
# -- for now - remove hours (e.g. use end_of_day and beginnig_of_day)
|
292
|
+
|
293
|
+
##
|
294
|
+
# note: start_at and end_at are dates ONLY (note datetime)
|
295
|
+
# - do NOT pass in hours etc. in query
|
296
|
+
# again use --> date.end_of_day, date.beginning_of_day
|
297
|
+
# new: not working: date.to_date, date.to_date
|
298
|
+
# will not find round if start_at same as date !! (in theory hours do not matter)
|
299
|
+
|
300
|
+
###
|
301
|
+
# hack:
|
302
|
+
# special case for sqlite3 (date compare not working reliable; use casts)
|
303
|
+
# fix: move to adapter_name to activerecord_utils as sqlite? or similar?
|
304
|
+
|
305
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.starts_with?( 'sqlite' )
|
306
|
+
logger.debug( " [sqlite] using sqlite-specific query for date compare for rounds finder" )
|
307
|
+
round = Round.where( 'event_id = ? AND ( julianday(start_at) <= julianday(?)'+
|
308
|
+
'AND julianday(end_at) >= julianday(?))',
|
309
|
+
@event.id, date.to_date, date.to_date).first
|
310
|
+
else # all other dbs (postgresql, mysql, etc.)
|
311
|
+
round = Round.where( 'event_id = ? AND (start_at <= ? AND end_at >= ?)',
|
312
|
+
@event.id, date.to_date, date.to_date).first
|
313
|
+
end
|
314
|
+
|
315
|
+
pp round
|
316
|
+
if round.nil?
|
317
|
+
logger.warn( " !!!! no round match found for date #{date}" )
|
318
|
+
pp Round.all
|
319
|
+
|
320
|
+
###################################
|
321
|
+
# -- try auto-adding matchday
|
322
|
+
round = Round.new
|
323
|
+
|
324
|
+
round_attribs = {
|
325
|
+
event_id: @event.id,
|
326
|
+
title: "Matchday #{date.to_date}",
|
327
|
+
pos: 999001+@patch_round_ids_pos.length, # e.g. 999<count> - 999001,999002,etc.
|
328
|
+
start_at: date.to_date,
|
329
|
+
end_at: date.to_date
|
330
|
+
}
|
331
|
+
|
332
|
+
logger.info( " auto-add round >Matchday #{date.to_date}<" )
|
333
|
+
logger.debug round_attribs.to_json
|
334
|
+
|
335
|
+
round.update_attributes!( round_attribs )
|
336
|
+
|
337
|
+
@patch_round_ids_pos << round.id # todo/check - add just id or "full" record as now - why? why not?
|
338
|
+
end
|
339
|
+
|
340
|
+
# store pos for auto-number next round if missing
|
341
|
+
# - note: only if greater/bigger than last; use max
|
342
|
+
# - note: last_round_pos might be nil - thus set to 0
|
343
|
+
if round.pos > 999000
|
344
|
+
# note: do NOT update last_round_pos for to-be-patched rounds
|
345
|
+
else
|
346
|
+
@last_round_pos = [round.pos,@last_round_pos||0].max
|
347
|
+
end
|
348
|
+
|
349
|
+
## note: will crash (round.pos) if round is nil
|
350
|
+
logger.debug( " using round #{round.pos} >#{round.title}< start_at: #{round.start_at}, end_at: #{round.end_at}" )
|
351
|
+
else
|
352
|
+
## use round from last round header
|
353
|
+
round = @round
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
### check if games exists
|
358
|
+
## with this teams in this round if yes only update
|
359
|
+
game = Game.find_by_round_id_and_team1_id_and_team2_id(
|
360
|
+
round.id, team1.id, team2.id
|
361
|
+
)
|
362
|
+
|
363
|
+
game_attribs = {
|
364
|
+
score1i: scores[0],
|
365
|
+
score2i: scores[1],
|
366
|
+
score1: scores[2],
|
367
|
+
score2: scores[3],
|
368
|
+
score1et: scores[4],
|
369
|
+
score2et: scores[5],
|
370
|
+
score1p: scores[6],
|
371
|
+
score2p: scores[7],
|
372
|
+
play_at: date,
|
373
|
+
play_at_v2: date_v2,
|
374
|
+
postponed: postponed,
|
375
|
+
knockout: round.knockout, ## note: for now always use knockout flag from round - why? why not??
|
376
|
+
ground_id: ground.present? ? ground.id : nil,
|
377
|
+
group_id: @group.present? ? @group.id : nil
|
378
|
+
}
|
379
|
+
|
380
|
+
game_attribs[ :pos ] = pos if pos.present?
|
381
|
+
|
382
|
+
####
|
383
|
+
# note: only update if any changes (or create if new record)
|
384
|
+
if game.present? &&
|
385
|
+
game.check_for_changes( game_attribs ) == false
|
386
|
+
logger.debug " skip update game #{game.id}; no changes found"
|
387
|
+
else
|
388
|
+
if game.present?
|
389
|
+
logger.debug "update game #{game.id}:"
|
390
|
+
else
|
391
|
+
logger.debug "create game:"
|
392
|
+
game = Game.new
|
393
|
+
|
394
|
+
more_game_attribs = {
|
395
|
+
round_id: round.id,
|
396
|
+
team1_id: team1.id,
|
397
|
+
team2_id: team2.id
|
398
|
+
}
|
399
|
+
|
400
|
+
## NB: use round.games.count for pos
|
401
|
+
## lets us add games out of order if later needed
|
402
|
+
more_game_attribs[ :pos ] = round.games.count+1 if pos.nil?
|
403
|
+
|
404
|
+
game_attribs = game_attribs.merge( more_game_attribs )
|
405
|
+
end
|
406
|
+
|
407
|
+
logger.debug game_attribs.to_json
|
408
|
+
game.update_attributes!( game_attribs )
|
409
|
+
end
|
410
|
+
|
411
|
+
@last_game = game # store for later reference (e.g. used for goals etc.)
|
412
|
+
=end
|
413
|
+
|
414
|
+
return true # game match found
|
415
|
+
end # method parse_game
|
416
|
+
|
417
|
+
|
418
|
+
|
419
|
+
def try_parse_date_header( line )
|
420
|
+
# note: clone line; for possible test do NOT modify in place for now
|
421
|
+
# note: returns true if parsed, false if no match
|
422
|
+
parse_date_header( line.dup )
|
423
|
+
end
|
424
|
+
|
425
|
+
def find_date!( line, opts={} )
|
426
|
+
## NB: lets us pass in start_at/end_at date (for event)
|
427
|
+
# for auto-complete year
|
428
|
+
|
429
|
+
# extract date from line
|
430
|
+
# and return it
|
431
|
+
# NB: side effect - removes date from line string
|
432
|
+
|
433
|
+
finder = DateFinder.new
|
434
|
+
finder.find!( line, opts )
|
435
|
+
end
|
436
|
+
|
437
|
+
def parse_date_header( line )
|
438
|
+
# note: returns true if parsed, false if no match
|
439
|
+
|
440
|
+
# line with NO teams plus include date e.g.
|
441
|
+
# [Fri Jun/17] or
|
442
|
+
# Jun/17 or
|
443
|
+
# Jun/17: etc.
|
444
|
+
|
445
|
+
@mapper_teams.map_teams!( line )
|
446
|
+
team_keys = @mapper_teams.find_teams!( line )
|
447
|
+
team1_key = team_keys[0]
|
448
|
+
team2_key = team_keys[1]
|
449
|
+
|
450
|
+
date = find_date!( line, start_at: @start_at )
|
451
|
+
|
452
|
+
if date && team1_key.nil? && team2_key.nil?
|
453
|
+
logger.debug( "date header line found: >#{line}<")
|
454
|
+
logger.debug( " date: #{date}")
|
455
|
+
|
456
|
+
@last_date = date # keep a reference for later use
|
457
|
+
return true
|
458
|
+
else
|
459
|
+
return false
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
|
464
|
+
|
465
|
+
end # class MatchParserSimpleV2
|
466
|
+
end # module SportDb
|