sportdb-formats 0.4.0 → 1.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 +4 -4
- data/Manifest.txt +24 -4
- data/Rakefile +3 -3
- data/lib/sportdb/formats.rb +25 -2
- data/lib/sportdb/formats/config.rb +40 -0
- data/lib/sportdb/formats/datafile.rb +42 -62
- data/lib/sportdb/formats/datafile_package.rb +160 -0
- data/lib/sportdb/formats/match/conf_parser.rb +120 -0
- data/lib/sportdb/formats/match/mapper.rb +319 -0
- data/lib/sportdb/formats/match/mapper_teams.rb +23 -0
- data/lib/sportdb/formats/match/match_parser.rb +659 -0
- data/lib/sportdb/formats/match/match_parser_auto_conf.rb +202 -0
- data/lib/sportdb/formats/name_helper.rb +84 -0
- data/lib/sportdb/formats/outline_reader.rb +53 -15
- data/lib/sportdb/formats/package.rb +172 -160
- data/lib/sportdb/formats/parser_helper.rb +81 -0
- data/lib/sportdb/formats/score/score_formats.rb +180 -0
- data/lib/sportdb/formats/score/score_parser.rb +196 -0
- data/lib/sportdb/formats/structs/country.rb +1 -43
- data/lib/sportdb/formats/structs/group.rb +25 -0
- data/lib/sportdb/formats/structs/league.rb +7 -26
- data/lib/sportdb/formats/structs/match.rb +72 -51
- data/lib/sportdb/formats/structs/round.rb +14 -4
- data/lib/sportdb/formats/structs/season.rb +3 -0
- data/lib/sportdb/formats/structs/team.rb +144 -0
- data/lib/sportdb/formats/version.rb +2 -2
- data/test/helper.rb +83 -1
- data/test/test_clubs.rb +3 -3
- data/test/test_conf.rb +65 -0
- data/test/test_datafile.rb +21 -30
- data/test/test_match.rb +0 -6
- data/test/test_match_auto.rb +72 -0
- data/test/test_match_auto_champs.rb +45 -0
- data/test/test_match_auto_euro.rb +37 -0
- data/test/test_match_auto_worldcup.rb +61 -0
- data/test/test_match_champs.rb +27 -0
- data/test/test_match_eng.rb +26 -0
- data/test/test_match_euro.rb +27 -0
- data/test/test_match_worldcup.rb +27 -0
- data/test/test_name_helper.rb +67 -0
- data/test/test_outline_reader.rb +3 -3
- data/test/test_package.rb +21 -2
- data/test/test_package_match.rb +78 -0
- data/test/test_scores.rb +67 -51
- metadata +32 -12
- data/lib/sportdb/formats/scores.rb +0 -253
- data/lib/sportdb/formats/structs/club.rb +0 -213
- data/test/test_club_helpers.rb +0 -63
- data/test/test_datafile_match.rb +0 -65
@@ -0,0 +1,659 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SportDb
|
4
|
+
|
5
|
+
class MatchParserSimpleV2 ## simple match parser for team match schedules
|
6
|
+
|
7
|
+
def self.parse( lines, teams, start: )
|
8
|
+
## todo/fix: add support for txt and lines
|
9
|
+
## check if lines_or_txt is an array or just a string
|
10
|
+
## use teams: like start: why? why not?
|
11
|
+
parser = new( lines, teams, start )
|
12
|
+
parser.parse
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
include Logging ## e.g. logger#debug, logger#info, etc.
|
17
|
+
include ParserHelper ## e.g. read_lines, etc.
|
18
|
+
|
19
|
+
|
20
|
+
def initialize( lines, teams, start )
|
21
|
+
# for convenience split string into lines
|
22
|
+
## note: removes/strips empty lines
|
23
|
+
## todo/check: change to text instead of array of lines - why? why not?
|
24
|
+
@lines = lines.is_a?( String ) ? read_lines( lines ) : lines
|
25
|
+
|
26
|
+
@mapper_teams = TeamMapper.new( teams )
|
27
|
+
@start = start
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def parse
|
32
|
+
@last_date = nil
|
33
|
+
@last_round = nil
|
34
|
+
@last_group = nil
|
35
|
+
|
36
|
+
@rounds = {}
|
37
|
+
@groups = {}
|
38
|
+
@matches = []
|
39
|
+
|
40
|
+
@warns = [] ## track list of warnings (unmatched lines) too - why? why not?
|
41
|
+
|
42
|
+
|
43
|
+
@lines.each do |line|
|
44
|
+
if is_goals?( line )
|
45
|
+
logger.debug "skipping matched goals line: >#{line}<"
|
46
|
+
elsif is_round_def?( line )
|
47
|
+
## todo/fix: add round definition (w begin n end date)
|
48
|
+
## todo: do not patch rounds with definition (already assume begin/end date is good)
|
49
|
+
## -- how to deal with matches that get rescheduled/postponed?
|
50
|
+
parse_round_def( line )
|
51
|
+
elsif is_round?( line )
|
52
|
+
parse_round_header( line )
|
53
|
+
elsif is_group_def?( line ) ## NB: group goes after round (round may contain group marker too)
|
54
|
+
### todo: add pipe (|) marker (required)
|
55
|
+
parse_group_def( line )
|
56
|
+
elsif is_group?( line )
|
57
|
+
## -- lets you set group e.g. Group A etc.
|
58
|
+
parse_group_header( line )
|
59
|
+
elsif try_parse_game( line )
|
60
|
+
# do nothing here
|
61
|
+
elsif try_parse_date_header( line )
|
62
|
+
# do nothing here
|
63
|
+
else
|
64
|
+
logger.warn "skipping line (no match found): >#{line}<"
|
65
|
+
@warns << line
|
66
|
+
end
|
67
|
+
end # lines.each
|
68
|
+
|
69
|
+
[@matches, @rounds.values, @groups.values]
|
70
|
+
end # method parse
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
def parse_group_header( line )
|
75
|
+
logger.debug "parsing group header line: >#{line}<"
|
76
|
+
|
77
|
+
# note: group header resets (last) round (allows, for example):
|
78
|
+
# e.g.
|
79
|
+
# Group Playoffs/Replays -- round header
|
80
|
+
# team1 team2 -- match
|
81
|
+
# Group B: -- group header
|
82
|
+
# team1 team2 - match (will get new auto-matchday! not last round)
|
83
|
+
@last_round = nil
|
84
|
+
|
85
|
+
title, pos = find_group_title_and_pos!( line )
|
86
|
+
|
87
|
+
logger.debug " title: >#{title}<"
|
88
|
+
logger.debug " pos: >#{pos}<"
|
89
|
+
logger.debug " line: >#{line}<"
|
90
|
+
|
91
|
+
group = @groups[ title ]
|
92
|
+
if group.nil?
|
93
|
+
puts "!! ERROR - no group def found for >#{title}<"
|
94
|
+
exit 1
|
95
|
+
end
|
96
|
+
|
97
|
+
# set group for games
|
98
|
+
@last_group = group
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse_group_def( line )
|
102
|
+
logger.debug "parsing group def line: >#{line}<"
|
103
|
+
|
104
|
+
@mapper_teams.map_teams!( line )
|
105
|
+
teams = @mapper_teams.find_teams!( line )
|
106
|
+
|
107
|
+
title, pos = find_group_title_and_pos!( line )
|
108
|
+
|
109
|
+
logger.debug " line: >#{line}<"
|
110
|
+
|
111
|
+
group = Import::Group.new( pos: pos,
|
112
|
+
title: title,
|
113
|
+
teams: teams.map {|team| team.title } )
|
114
|
+
|
115
|
+
@groups[ title ] = group
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def find_group_title_and_pos!( line )
|
120
|
+
## group pos - for now support single digit e.g 1,2,3 or letter e.g. A,B,C or HEX
|
121
|
+
## nb: (?:) = is for non-capturing group(ing)
|
122
|
+
|
123
|
+
## fix:
|
124
|
+
## get Group|Gruppe|Grupo from lang!!!! do NOT hardcode in place
|
125
|
+
|
126
|
+
## todo:
|
127
|
+
## check if Group A: or [Group A] works e.g. : or ] get matched by \b ???
|
128
|
+
regex = /(?:Group|Gruppe|Grupo)\s+((?:\d{1}|[A-Z]{1,3}))\b/
|
129
|
+
|
130
|
+
m = regex.match( line )
|
131
|
+
|
132
|
+
return [nil,nil] if m.nil?
|
133
|
+
|
134
|
+
pos = case m[1]
|
135
|
+
when 'A' then 1
|
136
|
+
when 'B' then 2
|
137
|
+
when 'C' then 3
|
138
|
+
when 'D' then 4
|
139
|
+
when 'E' then 5
|
140
|
+
when 'F' then 6
|
141
|
+
when 'G' then 7
|
142
|
+
when 'H' then 8
|
143
|
+
when 'I' then 9
|
144
|
+
when 'J' then 10
|
145
|
+
when 'K' then 11
|
146
|
+
when 'L' then 12
|
147
|
+
when 'HEX' then 666 # HEX for Hexagonal - todo/check: map to something else ??
|
148
|
+
else m[1].to_i
|
149
|
+
end
|
150
|
+
|
151
|
+
title = m[0]
|
152
|
+
|
153
|
+
logger.debug " title: >#{title}<"
|
154
|
+
logger.debug " pos: >#{pos}<"
|
155
|
+
|
156
|
+
line.sub!( regex, '[GROUP.TITLE+POS]' )
|
157
|
+
|
158
|
+
[title,pos]
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def parse_round_def( line )
|
163
|
+
logger.debug "parsing round def line: >#{line}<"
|
164
|
+
|
165
|
+
start_date = find_date!( line, start: @start )
|
166
|
+
end_date = find_date!( line, start: @start )
|
167
|
+
|
168
|
+
# note: if end_date missing -- assume start_date is (==) end_at
|
169
|
+
end_date = start_date if end_date.nil?
|
170
|
+
|
171
|
+
# note: - NOT needed; start_at and end_at are saved as date only (NOT datetime)
|
172
|
+
# set hours,minutes,secs to beginning and end of day (do NOT use default 12.00)
|
173
|
+
# e.g. use 00.00 and 23.59
|
174
|
+
# start_at = start_at.beginning_of_day
|
175
|
+
# end_at = end_at.end_of_day
|
176
|
+
|
177
|
+
# note: make sure start_at/end_at is date only (e.g. use start_at.to_date)
|
178
|
+
# sqlite3 saves datetime in date field as datetime, for example (will break date compares later!)
|
179
|
+
start_date = start_date.to_date
|
180
|
+
end_date = end_date.to_date
|
181
|
+
|
182
|
+
|
183
|
+
pos = find_round_pos!( line )
|
184
|
+
title = find_round_def_title!( line )
|
185
|
+
# NB: use extracted round title for knockout check
|
186
|
+
knockout_flag = is_knockout_round?( title )
|
187
|
+
|
188
|
+
|
189
|
+
logger.debug " start_date: #{start_date}"
|
190
|
+
logger.debug " end_date: #{end_date}"
|
191
|
+
logger.debug " pos: #{pos}"
|
192
|
+
logger.debug " title: >#{title}<"
|
193
|
+
logger.debug " knockout_flag: #{knockout_flag}"
|
194
|
+
|
195
|
+
logger.debug " line: >#{line}<"
|
196
|
+
|
197
|
+
#######################################
|
198
|
+
# todo/fix: add auto flag is false !!!! - why? why not?
|
199
|
+
round = Import::Round.new( pos: pos,
|
200
|
+
title: title,
|
201
|
+
start_date: start_date,
|
202
|
+
end_date: end_date,
|
203
|
+
knockout: knockout_flag,
|
204
|
+
auto: false )
|
205
|
+
|
206
|
+
@rounds[ title ] = round
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
|
211
|
+
def find_round_pos!( line )
|
212
|
+
# pass #1) extract optional round pos from line
|
213
|
+
# e.g. (1) - must start line
|
214
|
+
regex_pos = /^[ \t]*\((\d{1,3})\)[ \t]+/
|
215
|
+
|
216
|
+
# pass #2) find free standing number e.g. Matchday 3 or Round 5 or 3. Spieltag etc.
|
217
|
+
# note: /\b(\d{1,3})\b/
|
218
|
+
# will match -12
|
219
|
+
# thus, use space required - will NOT match -2 e.g. Group-2 Play-off
|
220
|
+
# note: allow 1. Runde n
|
221
|
+
# 1^ Giornata
|
222
|
+
regex_num = /(?:^|\s)(\d{1,3})(?:[.\^\s]|$)/
|
223
|
+
|
224
|
+
if line =~ regex_pos
|
225
|
+
logger.debug " pos: >#{$1}<"
|
226
|
+
|
227
|
+
line.sub!( regex_pos, '[ROUND.POS] ' ) ## NB: add back trailing space that got swallowed w/ regex -> [ \t]+
|
228
|
+
return $1.to_i
|
229
|
+
elsif line =~ regex_num
|
230
|
+
## assume number in title is pos (e.g. Jornada 3, 3 Runde etc.)
|
231
|
+
## NB: do NOT remove pos from string (will get removed by round title)
|
232
|
+
|
233
|
+
num = $1.to_i # note: clone capture; keep a copy (another regex follows; will redefine $1)
|
234
|
+
|
235
|
+
#### fix:
|
236
|
+
# use/make keywords required
|
237
|
+
# e.g. Round of 16 -> should NOT match 16!
|
238
|
+
# Spiel um Platz 3 (or 5) etc -> should NOT match 3!
|
239
|
+
# Round 16 - ok
|
240
|
+
# thus, check for required keywords
|
241
|
+
|
242
|
+
## quick hack for round of 16
|
243
|
+
# todo: mask match e.g. Round of xxx ... and try again - might include something
|
244
|
+
# reuse pattern for Group XX Replays for example
|
245
|
+
if line =~ /^\s*Round of \d{1,3}\b/
|
246
|
+
return nil
|
247
|
+
end
|
248
|
+
|
249
|
+
logger.debug " pos: >#{num}<"
|
250
|
+
return num
|
251
|
+
else
|
252
|
+
## fix: add logger.warn no round pos found in line
|
253
|
+
return nil
|
254
|
+
end
|
255
|
+
end # method find_round_pos!
|
256
|
+
|
257
|
+
def find_round_def_title!( line )
|
258
|
+
# assume everything before pipe (\) is the round title
|
259
|
+
# strip [ROUND.POS], todo:?? [ROUND.TITLE2]
|
260
|
+
|
261
|
+
# todo/fix: add title2 w/ // or / why? why not?
|
262
|
+
# -- strip / or / chars
|
263
|
+
|
264
|
+
buf = line.dup
|
265
|
+
logger.debug " find_round_def_title! line-before: >>#{buf}<<"
|
266
|
+
|
267
|
+
## cut-off everything after (including) pipe (|)
|
268
|
+
buf = buf[ 0...buf.index('|') ]
|
269
|
+
|
270
|
+
# e.g. remove [ROUND.POS], [ROUND.TITLE2], [GROUP.TITLE+POS] etc.
|
271
|
+
buf.gsub!( /\[[^\]]+\]/, '' ) ## fix: use helper for (re)use e.g. remove_match_placeholder/marker or similar?
|
272
|
+
# remove leading and trailing whitespace
|
273
|
+
buf.strip!
|
274
|
+
|
275
|
+
logger.debug " find_round_def_title! line-after: >>#{buf}<<"
|
276
|
+
|
277
|
+
logger.debug " title: >>#{buf}<<"
|
278
|
+
line.sub!( buf, '[ROUND.TITLE]' )
|
279
|
+
|
280
|
+
buf
|
281
|
+
end
|
282
|
+
|
283
|
+
def find_round_header_title!( line )
|
284
|
+
# assume everything left is the round title
|
285
|
+
# extract all other items first (round title2, round pos, group title n pos, etc.)
|
286
|
+
|
287
|
+
## todo/fix:
|
288
|
+
## cleanup method
|
289
|
+
## use buf.index( '//' ) to split string (see found_round_def)
|
290
|
+
## why? simpler why not?
|
291
|
+
## - do we currently allow groups if title2 present? add example if it works?
|
292
|
+
|
293
|
+
buf = line.dup
|
294
|
+
logger.debug " find_round_header_title! line-before: >>#{buf}<<"
|
295
|
+
|
296
|
+
buf.gsub!( /\[[^\]]+\]/, '' ) # e.g. remove [ROUND.POS], [ROUND.TITLE2], [GROUP.TITLE+POS] etc.
|
297
|
+
buf.strip! # remove leading and trailing whitespace
|
298
|
+
|
299
|
+
logger.debug " find_round_title! line-after: >>#{buf}<<"
|
300
|
+
|
301
|
+
### bingo - assume what's left is the round title
|
302
|
+
|
303
|
+
logger.debug " title: >>#{buf}<<"
|
304
|
+
line.sub!( buf, '[ROUND.TITLE]' )
|
305
|
+
|
306
|
+
buf
|
307
|
+
end
|
308
|
+
|
309
|
+
|
310
|
+
def parse_round_header( line )
|
311
|
+
logger.debug "parsing round header line: >#{line}<"
|
312
|
+
|
313
|
+
## todo/check/fix:
|
314
|
+
# make sure Round of 16 will not return pos 16 -- how? possible?
|
315
|
+
# add unit test too to verify
|
316
|
+
pos = find_round_pos!( line )
|
317
|
+
|
318
|
+
title = find_round_header_title!( line )
|
319
|
+
|
320
|
+
logger.debug " line: >#{line}<"
|
321
|
+
|
322
|
+
|
323
|
+
round = @rounds[ title ]
|
324
|
+
if round.nil? ## auto-add / create if missing
|
325
|
+
round = Import::Round.new( pos: pos,
|
326
|
+
title: title )
|
327
|
+
@rounds[ title ] = round
|
328
|
+
end
|
329
|
+
|
330
|
+
## todo/check: if pos match (MUST always match for now)
|
331
|
+
@last_round = round
|
332
|
+
@last_group = nil # note: reset group to no group - why? why not?
|
333
|
+
|
334
|
+
|
335
|
+
## NB: dummy/placeholder start_at, end_at date
|
336
|
+
## replace/patch after adding all games for round
|
337
|
+
|
338
|
+
=begin
|
339
|
+
round_attribs = {
|
340
|
+
title: title,
|
341
|
+
title2: title2,
|
342
|
+
knockout: knockout_flag
|
343
|
+
}
|
344
|
+
|
345
|
+
if pos > 999000
|
346
|
+
# no pos (e.g. will get autonumbered later) - try match by title for now
|
347
|
+
# e.g. lets us use title 'Group Replays', for example, multiple times
|
348
|
+
@round = Round.find_by_event_id_and_title( @event.id, title )
|
349
|
+
else
|
350
|
+
@round = Round.find_by_event_id_and_pos( @event.id, pos )
|
351
|
+
end
|
352
|
+
|
353
|
+
if @round.present?
|
354
|
+
logger.debug "update round #{@round.id}:"
|
355
|
+
else
|
356
|
+
logger.debug "create round:"
|
357
|
+
@round = Round.new
|
358
|
+
|
359
|
+
round_attribs = round_attribs.merge( {
|
360
|
+
event_id: @event.id,
|
361
|
+
pos: pos,
|
362
|
+
start_at: Date.parse('1911-11-11'),
|
363
|
+
end_at: Date.parse('1911-11-11')
|
364
|
+
})
|
365
|
+
end
|
366
|
+
|
367
|
+
logger.debug round_attribs.to_json
|
368
|
+
|
369
|
+
@round.update_attributes!( round_attribs )
|
370
|
+
|
371
|
+
@patch_round_ids_pos << @round.id if pos > 999000
|
372
|
+
### store list of round ids for patching start_at/end_at at the end
|
373
|
+
@patch_round_ids_dates << @round.id # todo/fix/check: check if round has definition (do NOT patch if definition (not auto-added) present)
|
374
|
+
=end
|
375
|
+
end
|
376
|
+
|
377
|
+
|
378
|
+
def find_score!( line )
|
379
|
+
# note: always call after find_dates !!!
|
380
|
+
# scores match date-like patterns!! e.g. 10-11 or 10:00 etc.
|
381
|
+
# -- note: score might have two digits too
|
382
|
+
|
383
|
+
ScoreFormats.find!( line )
|
384
|
+
end
|
385
|
+
|
386
|
+
def try_parse_game( line )
|
387
|
+
# note: clone line; for possible test do NOT modify in place for now
|
388
|
+
# note: returns true if parsed, false if no match
|
389
|
+
parse_game( line.dup )
|
390
|
+
end
|
391
|
+
|
392
|
+
|
393
|
+
def parse_game( line )
|
394
|
+
logger.debug "parsing game (fixture) line: >#{line}<"
|
395
|
+
|
396
|
+
## split by geo (@) - remove for now
|
397
|
+
## split into parts e.g. break using @ !!!
|
398
|
+
values = line.split( '@' )
|
399
|
+
line = values[0]
|
400
|
+
|
401
|
+
|
402
|
+
@mapper_teams.map_teams!( line ) ### todo/fix: limit mapping to two(2) teams - why? why not? might avoid matching @ Barcelona ??
|
403
|
+
teams = @mapper_teams.find_teams!( line )
|
404
|
+
team1 = teams[0]
|
405
|
+
team2 = teams[1]
|
406
|
+
|
407
|
+
## note: if we do NOT find two teams; return false - no match found
|
408
|
+
if team1.nil? || team2.nil?
|
409
|
+
logger.debug " no game match (two teams required) found for line: >#{line}<"
|
410
|
+
return false
|
411
|
+
end
|
412
|
+
|
413
|
+
## pos = find_game_pos!( line )
|
414
|
+
|
415
|
+
date = find_date!( line, start: @start )
|
416
|
+
|
417
|
+
###
|
418
|
+
# check if date found?
|
419
|
+
# note: ruby falsey is nil & false only (not 0 or empty array etc.)
|
420
|
+
if date
|
421
|
+
### check: use date_v2 if present? why? why not?
|
422
|
+
@last_date = date # keep a reference for later use
|
423
|
+
else
|
424
|
+
date = @last_date # no date found; (re)use last seen date
|
425
|
+
end
|
426
|
+
|
427
|
+
|
428
|
+
score = find_score!( line )
|
429
|
+
|
430
|
+
logger.debug " line: >#{line}<"
|
431
|
+
|
432
|
+
|
433
|
+
round = nil
|
434
|
+
if @last_round
|
435
|
+
round = @last_round
|
436
|
+
else
|
437
|
+
## find (first) matching round by date
|
438
|
+
@rounds.values.each do |round_rec|
|
439
|
+
## note: convert date to date only (no time) with to_date!!!
|
440
|
+
if (round_rec.start_date && round_rec.end_date) &&
|
441
|
+
(date.to_date >= round_rec.start_date &&
|
442
|
+
date.to_date <= round_rec.end_date)
|
443
|
+
round = round_rec
|
444
|
+
break
|
445
|
+
end
|
446
|
+
end
|
447
|
+
if round.nil?
|
448
|
+
puts "!! ERROR - no matching round found for match date:"
|
449
|
+
pp date
|
450
|
+
exit 1
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
|
455
|
+
## todo/check: scores are integers or strings?
|
456
|
+
|
457
|
+
## todo/check: pass along round and group refs or just string (canonical names) - why? why not?
|
458
|
+
|
459
|
+
@matches << Import::Match.new( date: date,
|
460
|
+
team1: team1, ## note: for now always use mapping value e.g. rec (NOT string e.g. team1.title)
|
461
|
+
team2: team2, ## note: for now always use mapping value e.g. rec (NOT string e.g. team2.title)
|
462
|
+
score: score,
|
463
|
+
round: round ? round.title : nil, ## note: for now always use string (assume unique canonical name for event)
|
464
|
+
group: @last_group ? @last_group.title : nil ) ## note: for now always use string (assume unique canonical name for event)
|
465
|
+
|
466
|
+
### todo: cache team lookups in hash?
|
467
|
+
|
468
|
+
=begin
|
469
|
+
team1 = Team.find_by_key!( team1_key )
|
470
|
+
team2 = Team.find_by_key!( team2_key )
|
471
|
+
|
472
|
+
@last_team1 = team1 # store for later use for goals etc.
|
473
|
+
@last_team2 = team2
|
474
|
+
|
475
|
+
|
476
|
+
if @round.nil?
|
477
|
+
## no round header found; calculate round from date
|
478
|
+
|
479
|
+
###
|
480
|
+
## todo/fix: add some unit tests for round look up
|
481
|
+
# fix: use date_v2 if present!! (old/original date; otherwise use date)
|
482
|
+
|
483
|
+
#
|
484
|
+
# fix: check - what to do with hours e.g. start_at use 00:00 and for end_at use 23.59 ??
|
485
|
+
# -- for now - remove hours (e.g. use end_of_day and beginnig_of_day)
|
486
|
+
|
487
|
+
##
|
488
|
+
# note: start_at and end_at are dates ONLY (note datetime)
|
489
|
+
# - do NOT pass in hours etc. in query
|
490
|
+
# again use --> date.end_of_day, date.beginning_of_day
|
491
|
+
# new: not working: date.to_date, date.to_date
|
492
|
+
# will not find round if start_at same as date !! (in theory hours do not matter)
|
493
|
+
|
494
|
+
###
|
495
|
+
# hack:
|
496
|
+
# special case for sqlite3 (date compare not working reliable; use casts)
|
497
|
+
# fix: move to adapter_name to activerecord_utils as sqlite? or similar?
|
498
|
+
|
499
|
+
if ActiveRecord::Base.connection.adapter_name.downcase.starts_with?( 'sqlite' )
|
500
|
+
logger.debug( " [sqlite] using sqlite-specific query for date compare for rounds finder" )
|
501
|
+
round = Round.where( 'event_id = ? AND ( julianday(start_at) <= julianday(?)'+
|
502
|
+
'AND julianday(end_at) >= julianday(?))',
|
503
|
+
@event.id, date.to_date, date.to_date).first
|
504
|
+
else # all other dbs (postgresql, mysql, etc.)
|
505
|
+
round = Round.where( 'event_id = ? AND (start_at <= ? AND end_at >= ?)',
|
506
|
+
@event.id, date.to_date, date.to_date).first
|
507
|
+
end
|
508
|
+
|
509
|
+
pp round
|
510
|
+
if round.nil?
|
511
|
+
logger.warn( " !!!! no round match found for date #{date}" )
|
512
|
+
pp Round.all
|
513
|
+
|
514
|
+
###################################
|
515
|
+
# -- try auto-adding matchday
|
516
|
+
round = Round.new
|
517
|
+
|
518
|
+
round_attribs = {
|
519
|
+
event_id: @event.id,
|
520
|
+
title: "Matchday #{date.to_date}",
|
521
|
+
pos: 999001+@patch_round_ids_pos.length, # e.g. 999<count> - 999001,999002,etc.
|
522
|
+
start_at: date.to_date,
|
523
|
+
end_at: date.to_date
|
524
|
+
}
|
525
|
+
|
526
|
+
logger.info( " auto-add round >Matchday #{date.to_date}<" )
|
527
|
+
logger.debug round_attribs.to_json
|
528
|
+
|
529
|
+
round.update_attributes!( round_attribs )
|
530
|
+
|
531
|
+
@patch_round_ids_pos << round.id # todo/check - add just id or "full" record as now - why? why not?
|
532
|
+
end
|
533
|
+
|
534
|
+
# store pos for auto-number next round if missing
|
535
|
+
# - note: only if greater/bigger than last; use max
|
536
|
+
# - note: last_round_pos might be nil - thus set to 0
|
537
|
+
if round.pos > 999000
|
538
|
+
# note: do NOT update last_round_pos for to-be-patched rounds
|
539
|
+
else
|
540
|
+
@last_round_pos = [round.pos,@last_round_pos||0].max
|
541
|
+
end
|
542
|
+
|
543
|
+
## note: will crash (round.pos) if round is nil
|
544
|
+
logger.debug( " using round #{round.pos} >#{round.title}< start_at: #{round.start_at}, end_at: #{round.end_at}" )
|
545
|
+
else
|
546
|
+
## use round from last round header
|
547
|
+
round = @round
|
548
|
+
end
|
549
|
+
|
550
|
+
|
551
|
+
### check if games exists
|
552
|
+
## with this teams in this round if yes only update
|
553
|
+
game = Game.find_by_round_id_and_team1_id_and_team2_id(
|
554
|
+
round.id, team1.id, team2.id
|
555
|
+
)
|
556
|
+
|
557
|
+
game_attribs = {
|
558
|
+
score1i: scores[0],
|
559
|
+
score2i: scores[1],
|
560
|
+
score1: scores[2],
|
561
|
+
score2: scores[3],
|
562
|
+
score1et: scores[4],
|
563
|
+
score2et: scores[5],
|
564
|
+
score1p: scores[6],
|
565
|
+
score2p: scores[7],
|
566
|
+
play_at: date,
|
567
|
+
play_at_v2: date_v2,
|
568
|
+
postponed: postponed,
|
569
|
+
knockout: round.knockout, ## note: for now always use knockout flag from round - why? why not??
|
570
|
+
ground_id: ground.present? ? ground.id : nil,
|
571
|
+
group_id: @group.present? ? @group.id : nil
|
572
|
+
}
|
573
|
+
|
574
|
+
game_attribs[ :pos ] = pos if pos.present?
|
575
|
+
|
576
|
+
####
|
577
|
+
# note: only update if any changes (or create if new record)
|
578
|
+
if game.present? &&
|
579
|
+
game.check_for_changes( game_attribs ) == false
|
580
|
+
logger.debug " skip update game #{game.id}; no changes found"
|
581
|
+
else
|
582
|
+
if game.present?
|
583
|
+
logger.debug "update game #{game.id}:"
|
584
|
+
else
|
585
|
+
logger.debug "create game:"
|
586
|
+
game = Game.new
|
587
|
+
|
588
|
+
more_game_attribs = {
|
589
|
+
round_id: round.id,
|
590
|
+
team1_id: team1.id,
|
591
|
+
team2_id: team2.id
|
592
|
+
}
|
593
|
+
|
594
|
+
## NB: use round.games.count for pos
|
595
|
+
## lets us add games out of order if later needed
|
596
|
+
more_game_attribs[ :pos ] = round.games.count+1 if pos.nil?
|
597
|
+
|
598
|
+
game_attribs = game_attribs.merge( more_game_attribs )
|
599
|
+
end
|
600
|
+
|
601
|
+
logger.debug game_attribs.to_json
|
602
|
+
game.update_attributes!( game_attribs )
|
603
|
+
end
|
604
|
+
|
605
|
+
@last_game = game # store for later reference (e.g. used for goals etc.)
|
606
|
+
=end
|
607
|
+
|
608
|
+
return true # game match found
|
609
|
+
end # method parse_game
|
610
|
+
|
611
|
+
|
612
|
+
|
613
|
+
def try_parse_date_header( line )
|
614
|
+
# note: clone line; for possible test do NOT modify in place for now
|
615
|
+
# note: returns true if parsed, false if no match
|
616
|
+
parse_date_header( line.dup )
|
617
|
+
end
|
618
|
+
|
619
|
+
def find_date!( line, start: )
|
620
|
+
## NB: lets us pass in start_at/end_at date (for event)
|
621
|
+
# for auto-complete year
|
622
|
+
|
623
|
+
# extract date from line
|
624
|
+
# and return it
|
625
|
+
# NB: side effect - removes date from line string
|
626
|
+
DateFormats.find!( line, start: start )
|
627
|
+
end
|
628
|
+
|
629
|
+
|
630
|
+
def parse_date_header( line )
|
631
|
+
# note: returns true if parsed, false if no match
|
632
|
+
|
633
|
+
# line with NO teams plus include date e.g.
|
634
|
+
# [Fri Jun/17] or
|
635
|
+
# Jun/17 or
|
636
|
+
# Jun/17: etc.
|
637
|
+
|
638
|
+
@mapper_teams.map_teams!( line )
|
639
|
+
teams = @mapper_teams.find_teams!( line )
|
640
|
+
team1 = teams[0]
|
641
|
+
team2 = teams[1]
|
642
|
+
|
643
|
+
date = find_date!( line, start: @start )
|
644
|
+
|
645
|
+
if date && team1.nil? && team2.nil?
|
646
|
+
logger.debug( "date header line found: >#{line}<")
|
647
|
+
logger.debug( " date: #{date}")
|
648
|
+
|
649
|
+
@last_date = date # keep a reference for later use
|
650
|
+
return true
|
651
|
+
else
|
652
|
+
return false
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
|
657
|
+
|
658
|
+
end # class MatchParserSimpleV2
|
659
|
+
end # module SportDb
|