sportdb-models 1.14.0 → 1.14.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.
@@ -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
-