sportdb-models 1.14.0 → 1.14.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,320 @@
1
+ # encoding: UTF-8
2
+
3
+ ##
4
+ #
5
+ # todo/fix: cleanup, remove stuff not needed for "simple" rsssf format/style
6
+ #
7
+ ## for now lets only support leagues with rounds (no cups/knockout rounds n groups)
8
+ ## (re)add later when needed (e.g. for playoffs etc.)
9
+
10
+
11
+ module SportDb
12
+
13
+
14
+ class RsssfGameReader
15
+
16
+ include LogUtils::Logging
17
+
18
+ ## make models available by default with namespace
19
+ # e.g. lets you use Usage instead of Model::Usage
20
+ include Models
21
+
22
+ ## value helpers e.g. is_year?, is_taglist? etc.
23
+ include TextUtils::ValueHelper
24
+
25
+ include FixtureHelpers
26
+
27
+ ##
28
+ ## todo: add from_file and from_zip too
29
+
30
+ def self.from_string( event_key, text )
31
+ ### fix - fix -fix:
32
+ ## change event to event_or_event_key !!!!! - allow event_key as string passed in
33
+ self.new( event_key, text )
34
+ end
35
+
36
+ def initialize( event_key, text )
37
+ ### fix - fix -fix:
38
+ ## change event to event_or_event_key !!!!! - allow event_key as string passed in
39
+
40
+ ## todo/fix: how to add opts={} ???
41
+ @event_key = event_key
42
+ @text = text
43
+ end
44
+
45
+
46
+ def read
47
+ ## note: assume active activerecord connection
48
+ @event = Event.find_by!( key: @event_key )
49
+
50
+ logger.debug "Event #{@event.key} >#{@event.title}<"
51
+
52
+ @team_mapper = TextUtils::TitleMapper.new( @event.teams, 'team' )
53
+
54
+ ## reset cached values
55
+ @patch_round_ids = []
56
+
57
+ @last_round = nil
58
+ @last_date = nil
59
+
60
+ ## always use english (en) for now
61
+ SportDb.lang.lang = 'en'
62
+
63
+ reader = LineReader.from_string( @text )
64
+ parse_fixtures( reader )
65
+ end # method load_fixtures
66
+
67
+
68
+ def parse_round_header( line )
69
+
70
+ ## todo/fix:
71
+ ## simplify - for now round number always required
72
+ # e.g. no auto-calculation supported here
73
+ # fail if round found w/o number/pos !!!
74
+ #
75
+ # also remove knockout flag for now (set to always false for now)
76
+
77
+ logger.debug "parsing round header line: >#{line}<"
78
+
79
+ ### todo/fix/check: move cut off optional comment in reader for all lines? why? why not?
80
+ cut_off_end_of_line_comment!( line ) # cut off optional comment starting w/ #
81
+
82
+ ## check for date in header first e.g. Round 36 [Jul 20] !!
83
+ ## avoid "conflict" with getting "wrong" round number from date etc.
84
+ date = find_rsssf_date!( line, start_at: @event.start_at )
85
+ if date
86
+ @last_date = date
87
+ end
88
+
89
+ ## todo/check/fix:
90
+ # make sure Round of 16 will not return pos 16 -- how? possible?
91
+ # add unit test too to verify
92
+ pos = find_round_pos!( line )
93
+
94
+ ## check if pos available; if not auto-number/calculate
95
+ if pos.nil?
96
+ logger.error( " no round pos found in line >#{line}<; round pos required in rsssf; sorry" )
97
+ fail( "round pos required in rsssf; sorry")
98
+ end
99
+
100
+ title = find_round_header_title!( line )
101
+
102
+ ## Note: use extracted round title for knockout check
103
+ ## knockout_flag = is_knockout_round?( title )
104
+
105
+ logger.debug " line: >#{line}<"
106
+
107
+ ## Note: dummy/placeholder start_at, end_at date
108
+ ## replace/patch after adding all games for round
109
+
110
+ round_attribs = {
111
+ title: title,
112
+ title2: nil,
113
+ knockout: false
114
+ }
115
+
116
+ round = Round.find_by( event_id: @event.id,
117
+ pos: pos )
118
+
119
+ if round.present?
120
+ logger.debug "update round #{round.id}:"
121
+ else
122
+ logger.debug "create round:"
123
+ round = Round.new
124
+
125
+ round_attribs = round_attribs.merge( {
126
+ event_id: @event.id,
127
+ pos: pos,
128
+ start_at: Date.parse('1911-11-11'),
129
+ end_at: Date.parse('1911-11-11')
130
+ })
131
+ end
132
+
133
+ logger.debug round_attribs.to_json
134
+
135
+ round.update_attributes!( round_attribs )
136
+
137
+ ### store list of round ids for patching start_at/end_at at the end
138
+ @patch_round_ids << round.id
139
+ @last_round = round ## keep track of last seen round for matches that follow etc.
140
+ end
141
+
142
+
143
+ def try_parse_game( line )
144
+ # note: clone line; for possible test do NOT modify in place for now
145
+ # note: returns true if parsed, false if no match
146
+ parse_game( line.dup )
147
+ end
148
+
149
+ def parse_game( line )
150
+ logger.debug "parsing game (fixture) line: >#{line}<"
151
+
152
+ @team_mapper.map_titles!( line )
153
+ team1_key = @team_mapper.find_key!( line )
154
+ team2_key = @team_mapper.find_key!( line )
155
+
156
+ ## note: if we do NOT find two teams; return false - no match found
157
+ if team1_key.nil? || team2_key.nil?
158
+ logger.debug " no game match (two teams required) found for line: >#{line}<"
159
+ return false
160
+ end
161
+
162
+ date = find_rsssf_date!( line, start_at: @event.start_at )
163
+
164
+ ###
165
+ # check if date found?
166
+ # note: ruby falsey is nil & false only (not 0 or empty array etc.)
167
+ if date
168
+ @last_date = date # keep a reference for later use
169
+ else
170
+ date = @last_date # no date found; (re)use last seen date
171
+ end
172
+
173
+ ## fix/todo: use find_rsssf_scores!( line )
174
+ ## use rsssf specific score finder!!!
175
+ scores = find_scores!( line )
176
+
177
+ logger.debug " line: >#{line}<"
178
+
179
+
180
+ ### todo: cache team lookups in hash?
181
+ team1 = Team.find_by!( key: team1_key )
182
+ team2 = Team.find_by!( key: team2_key )
183
+
184
+ round = @last_round
185
+
186
+ ### check if games exists
187
+ ## with this teams in this round if yes only update
188
+ game = Game.find_by( round_id: round.id,
189
+ team1_id: team1.id,
190
+ team2_id: team2.id )
191
+
192
+ game_attribs = {
193
+ score1: scores[0],
194
+ score2: scores[1],
195
+ score1et: scores[2],
196
+ score2et: scores[3],
197
+ score1p: scores[4],
198
+ score2p: scores[5],
199
+ play_at: date,
200
+ play_at_v2: nil,
201
+ postponed: false,
202
+ knockout: false, ## round.knockout, ## note: for now always use knockout flag from round - why? why not??
203
+ ground_id: nil,
204
+ group_id: nil
205
+ }
206
+
207
+ if game.present?
208
+ logger.debug "update game #{game.id}:"
209
+ else
210
+ logger.debug "create game:"
211
+ game = Game.new
212
+
213
+ ## Note: use round.games.count for pos
214
+ ## lets us add games out of order if later needed
215
+ more_game_attribs = {
216
+ round_id: round.id,
217
+ team1_id: team1.id,
218
+ team2_id: team2.id,
219
+ pos: round.games.count+1
220
+ }
221
+ game_attribs = game_attribs.merge( more_game_attribs )
222
+ end
223
+
224
+ logger.debug game_attribs.to_json
225
+ game.update_attributes!( game_attribs )
226
+
227
+ return true # game match found
228
+ end # method parse_game
229
+
230
+
231
+ def try_parse_date_header( line )
232
+ # note: clone line; for possible test do NOT modify in place for now
233
+ # note: returns true if parsed, false if no match
234
+ parse_date_header( line.dup )
235
+ end
236
+
237
+ def parse_date_header( line )
238
+ # note: returns true if parsed, false if no match
239
+
240
+ # line with NO teams plus include date e.g.
241
+ # [Jun 17] etc.
242
+
243
+ @team_mapper.map_titles!( line )
244
+ team1_key = @team_mapper.find_key!( line )
245
+ team2_key = @team_mapper.find_key!( line )
246
+
247
+ date = find_rsssf_date!( line, start_at: @event.start_at )
248
+
249
+ if date && team1_key.nil? && team2_key.nil?
250
+ logger.debug( "date header line found: >#{line}<")
251
+ logger.debug( " date: #{date}")
252
+
253
+ @last_date = date # keep a reference for later use
254
+ return true
255
+ else
256
+ return false
257
+ end
258
+ end
259
+
260
+
261
+
262
+ def parse_fixtures( reader )
263
+
264
+ reader.each_line do |line|
265
+
266
+ ## fix: use inline/simpler is_rsssf_round?
267
+ if is_round?( line )
268
+ parse_round_header( line )
269
+ elsif try_parse_game( line )
270
+ # do nothing here
271
+ elsif try_parse_date_header( line )
272
+ # do nothing here
273
+ else
274
+ logger.info "skipping line (no match found): >#{line}<"
275
+ end
276
+ end # lines.each
277
+
278
+ ###########################
279
+ # backtrack and patch round dates (start_at/end_at)
280
+
281
+ unless @patch_round_ids.empty?
282
+ ###
283
+ # note: use uniq - to allow multiple round headers (possible?)
284
+
285
+ Round.find( @patch_round_ids.uniq ).each do |r|
286
+ logger.debug "patch round start_at/end_at date for #{r.title}:"
287
+
288
+ ## note:
289
+ ## will add "scope" pos first e.g
290
+ #
291
+ ## SELECT "games".* FROM "games" WHERE "games"."round_id" = ?
292
+ # ORDER BY pos, play_at asc [["round_id", 7]]
293
+ # thus will NOT order by play_at but by pos first!!!
294
+ # =>
295
+ # need to unscope pos!!! or use unordered_games - games_by_play_at_date etc.??
296
+ # thus use reorder()!!! - not just order('play_at asc')
297
+
298
+ games = r.games.reorder( 'play_at asc' ).all
299
+
300
+ ## skip rounds w/ no games
301
+
302
+ ## todo/check/fix: what's the best way for checking assoc w/ 0 recs?
303
+ next if games.size == 0
304
+
305
+ # note: make sure start_at/end_at is date only (e.g. use play_at.to_date)
306
+ # sqlite3 saves datetime in date field as datetime, for example (will break date compares later!)
307
+
308
+ round_attribs = {
309
+ start_at: games[0].play_at.to_date, # use games.first ?
310
+ end_at: games[-1].play_at.to_date # use games.last ? why? why not?
311
+ }
312
+
313
+ logger.debug round_attribs.to_json
314
+ r.update_attributes!( round_attribs )
315
+ end
316
+ end
317
+ end # method parse_fixtures
318
+
319
+ end # class RsssfGameReader
320
+ end # module SportDb
@@ -21,6 +21,12 @@ module SportDb
21
21
  finder.find!( line, opts )
22
22
  end
23
23
 
24
+ def find_rsssf_date!( line, opts={} )
25
+ finder = RsssfDateFinder.new
26
+ finder.find!( line, opts )
27
+ end
28
+
29
+
24
30
  end # module FixtureHelpers
25
31
  end # module SportDb
26
32
 
@@ -4,7 +4,7 @@ module SportDb
4
4
 
5
5
  MAJOR = 1 ## todo: namespace inside version or something - why? why not??
6
6
  MINOR = 14
7
- PATCH = 0
7
+ PATCH = 1
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
@@ -0,0 +1,18 @@
1
+ #########################################################################
2
+ # Österreichische Bundesliga 2014/15 (103. Meisterschaft / 41. Saison)
3
+
4
+ league: at
5
+ season: 2014/15
6
+ start_at: 2014-07-19
7
+
8
+ 10 teams:
9
+ - salzburg
10
+ - rapid
11
+ - groedig
12
+ - austria
13
+ - sturm
14
+ - ried
15
+ - wac
16
+ - wrneustadt
17
+ - admira
18
+ - altach
@@ -5,15 +5,12 @@ league: at
5
5
  season: 2015/16
6
6
  start_at: 2015-07-25
7
7
 
8
- # num/edition - 104 or 42 ?? ##
9
- # note: timezone for games (play_at) is *always* CET (central european time)
10
-
11
8
  fixtures:
12
9
  - 1-bundesliga-i
13
10
  - 1-bundesliga-ii
14
11
 
15
12
 
16
- 10 Teams:
13
+ 10 teams:
17
14
  - RB Salzburg
18
15
  - Rapid Wien
19
16
  - Sturm Graz
@@ -25,4 +22,3 @@ fixtures:
25
22
  - SV Grödig
26
23
  - SV Mattersburg ## Aufsteiger
27
24
 
28
-
@@ -1,24 +1,17 @@
1
1
  #######################
2
2
  # Bundesliga 2013/14
3
-
4
- # 10 Teams
5
-
6
- # NB: 2013/14
7
3
  #
8
- # new team | SV Grödig
9
- # old team (moved to teams_2) | SV Mattersburg
10
-
11
- # NB: use three letter codes from bundesliga.at
4
+ # 10 Teams
12
5
 
13
6
 
14
7
  [austria]
15
- FK Austria Wien|Austria Wien, 1911, AUS
8
+ FK Austria Wien|Austria Wien|Austria, 1911, AUS
16
9
  www.fk-austria.at
17
10
  Fischhofgasse 12 // 1100 Wien
18
11
  city:wien
19
12
 
20
13
  [rapid]
21
- SK Rapid Wien|Rapid Wien, 1899, RAP
14
+ SK Rapid Wien|Rapid Wien|Rapid, 1899, RAP
22
15
  www.skrapid.at
23
16
  Keisslergasse 3 // 1140 Wien
24
17
  city:wien
@@ -38,32 +31,32 @@
38
31
 
39
32
 
40
33
  [salzburg]
41
- FC RB Salzburg|RB Salzburg|Red Bull Salzburg, 1933, RBS
34
+ FC RB Salzburg|RB Salzburg|Red Bull Salzburg|Salzburg, 1933, RBS
42
35
  www.redbulls.com/soccer/salzburg
43
36
  Stadionstraße 2/3 // 5071 Wals-Siezenheim
44
37
  city:salzburg
45
38
 
46
39
  [groedig]
47
- SV Grödig, 1948, SVG
40
+ SV Grödig|Grödig, 1948, SVG
48
41
  www.sv-groedig.at
49
42
  Prötschhofstrasse 26 // 5082 Grödig
50
43
  city:groedig
51
44
 
52
45
 
53
46
  [sturm]
54
- SK Sturm Graz|Sturm Graz, 1909, STU
47
+ SK Sturm Graz|Sturm Graz|Sturm, 1909, STU
55
48
  www.sksturm.at
56
49
  Sternäckerweg 118 // 8042 Graz
57
50
  city:graz
58
51
 
59
52
  [wac]
60
- Wolfsberger AC|RZ Pellets WAC, 1931, WAC
53
+ Wolfsberger AC|RZ Pellets WAC|Wolfsberg, 1931, WAC
61
54
  www.rzpelletswac.at
62
55
  Don Bosco Weg 1 // 9400 Wolfsberg
63
56
  city:wolfsberg
64
57
 
65
58
  [ried]
66
- SV Ried|SV Josko Ried|SV Josko Fenster Ried, 1912, RIE
59
+ SV Ried|SV Josko Ried|SV Josko Fenster Ried|Ried, 1912, RIE
67
60
  www.svried.at
68
61
  Volksfestplatz 2 // 4910 Ried im Innkreis
69
62
  city:ried
@@ -1,14 +1,12 @@
1
1
  ########################
2
2
  # Erste Liga 2013/14
3
3
  #
4
- # 10 Teams
4
+ # 10 Teams
5
5
 
6
- # NB: use three letter codes from bundesliga.at
7
6
 
7
+ mattersburg, SV Mattersburg|Mattersburg, Erste Liga/Bgld., MAT, city:mattersburg # from Bundesliga
8
8
 
9
- mattersburg, SV Mattersburg, Erste Liga/Bgld., MAT, city:mattersburg # from Bundesliga
10
-
11
- altach, SCR Altach|SC Rheindorf Altach, Erste Liga/Vbg., ALT, city:altach
9
+ altach, SCR Altach|SC Rheindorf Altach|Altach, Erste Liga/Vbg., ALT, city:altach
12
10
  austrial, SC Austria Lustenau|Austria Lustenau, Erste Liga/Vbg., ALU, city:lustenau
13
11
  stpoelten, SKN St. Pölten, Erste Liga/NÖ, SKN, city:stpoelten
14
12
  ksv, Kapfenberger SV|Kapfenberger SV 1919|KSV 1919, Erste Liga/Stmk., KSV, city:kapfenberg
@@ -21,14 +19,3 @@ parndorf, SC/ESV Parndorf 1919|SC-ESV Parndorf 1919|SC/ESV Parndorf, Erste Li
21
19
  liefering, FC Liefering, Erste Liga/Sbg., LIE, city:salzburg # from Regionalliga West - Liefering - Stadtteil von Salzburg
22
20
 
23
21
 
24
- # NB: 2013/14
25
- # bundesliga:
26
- # - new team SV Mattersburg
27
- # - old team (moved to teams.1) SV Grödig
28
- #
29
- # erste liga:
30
- # - new team SC/ESV Parndorf 1919
31
- # FC Liefering
32
- # - old team (moved to teams.3) FC Blau-Weiß Linz
33
- # FC Lustenau 1907
34
-