sportdb-market 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sportdb/market/market.rb +12 -2
- data/lib/sportdb/market/reader.rb +274 -1
- data/lib/sportdb/market/version.rb +1 -1
- metadata +3 -3
@@ -15,6 +15,16 @@ require 'sportdb/market/reader'
|
|
15
15
|
|
16
16
|
|
17
17
|
module SportDB::Market
|
18
|
-
puts "hello from sportdb-market, version #{VERSION}"
|
19
|
-
end
|
20
18
|
|
19
|
+
def self.banner
|
20
|
+
"sportdb-market #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.root
|
24
|
+
"#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}"
|
25
|
+
end
|
26
|
+
|
27
|
+
puts "SportDB::Market.banner: >>#{SportDB::Market.banner}<<"
|
28
|
+
puts "SportDB::Market.root: >>#{SportDB::Market.root}<<"
|
29
|
+
|
30
|
+
end
|
@@ -1,2 +1,275 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
module SportDB::Market
|
3
|
+
|
4
|
+
### load quotes from plain text files
|
5
|
+
|
6
|
+
class Reader
|
7
|
+
|
8
|
+
## make models available in sportdb module by default with namespace
|
9
|
+
# e.g. lets you use Team instead of Models::Team
|
10
|
+
include SportDB::Models
|
11
|
+
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@logger = Logger.new(STDOUT)
|
15
|
+
@logger.level = Logger::INFO
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :logger
|
19
|
+
|
20
|
+
def load_with_include_path( service_key, event_key, name, include_path ) # load from file system
|
21
|
+
path = "#{include_path}/#{name}.txt"
|
22
|
+
|
23
|
+
puts "*** parsing data '#{name}' (#{path})..."
|
24
|
+
|
25
|
+
code = File.read( path )
|
26
|
+
|
27
|
+
load_worker( service_key, event_key, code )
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_builtin( service_key, event_key, name ) # load from gem (built-in)
|
31
|
+
path = "#{SportDB::Market.root}/db/#{name}.txt"
|
32
|
+
|
33
|
+
puts "*** parsing data '#{name}' (#{path})..."
|
34
|
+
|
35
|
+
code = File.read( path )
|
36
|
+
|
37
|
+
load_worker( service_key, event_key, code )
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def load_worker( service_key, event_key, data )
|
42
|
+
|
43
|
+
## assume active activerecord connection
|
44
|
+
##
|
45
|
+
|
46
|
+
@service = Service.find_by_key!( service_key )
|
47
|
+
@event = Event.find_by_key!( event_key )
|
48
|
+
|
49
|
+
puts "Quote Service #{@service.key} >#{@service.title}<"
|
50
|
+
puts "Event #{@event.key} >#{@event.title}<"
|
51
|
+
|
52
|
+
|
53
|
+
## build known teams table w/ synonyms e.g.
|
54
|
+
#
|
55
|
+
# nb: synonyms can be a regex not just a literal string
|
56
|
+
# [[ 'wolfsbrug', [ 'VfL Wolfsburg' ]],
|
57
|
+
# [ 'augsburg', [ 'FC Augsburg', 'Augi2', 'Augi3' ]],
|
58
|
+
# [ 'stuttgart', [ 'VfB Stuttgart' ]] ]
|
59
|
+
|
60
|
+
### todo/fix: move known_teams code to model and reuse in readers!!
|
61
|
+
## do NOT duplicate
|
62
|
+
|
63
|
+
@known_teams = []
|
64
|
+
|
65
|
+
@event.teams.each_with_index do |team,index|
|
66
|
+
|
67
|
+
titles = []
|
68
|
+
titles << team.title
|
69
|
+
titles += team.synonyms.split('|') if team.synonyms.present?
|
70
|
+
|
71
|
+
## NB: sort here by length (largest goes first - best match)
|
72
|
+
# exclude tag and key (key should always go last)
|
73
|
+
titles = titles.sort { |left,right| right.length <=> left.length }
|
74
|
+
|
75
|
+
titles << team.tag if team.tag.present?
|
76
|
+
titles << team.key
|
77
|
+
|
78
|
+
@known_teams << [ team.key, titles ]
|
79
|
+
|
80
|
+
puts " Team[#{index+1}] #{team.key} >#{titles.join('|')}<"
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
parse_quotes( data )
|
85
|
+
|
86
|
+
puts 'Done.'
|
87
|
+
|
88
|
+
end # method load
|
89
|
+
|
90
|
+
|
91
|
+
def is_round?( line )
|
92
|
+
line =~ /Spieltag|Runde/
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_round_pos!( line )
|
96
|
+
regex = /\b(\d+)\b/
|
97
|
+
|
98
|
+
if line =~ regex
|
99
|
+
value = $1.to_i
|
100
|
+
puts " pos: >#{value}<"
|
101
|
+
|
102
|
+
line.sub!( regex, '[POS]' )
|
103
|
+
|
104
|
+
return value
|
105
|
+
else
|
106
|
+
return nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def find_quotes!( line )
|
111
|
+
# extract quotes triplet from line
|
112
|
+
# and return it
|
113
|
+
# NB: side effect - removes quotes triplet from line string
|
114
|
+
|
115
|
+
# e.g. 1,55 3,55 6,44
|
116
|
+
|
117
|
+
# NB: (?:) is used for non-capturing grouping
|
118
|
+
|
119
|
+
## regex1 uses point., e.g. 1.55 for separator
|
120
|
+
## regex2 uses comma-, e.g. 1,55 for separator
|
121
|
+
|
122
|
+
|
123
|
+
regex1 = /[ \t]+(\d{1,3}(?:\.\d{1,3})?)[ \t]+(\d{1,3}(?:\.\d{1,3})?)[ \t]+(\d{1,3}(?:\.\d{1,3})?)/
|
124
|
+
regex2 = /[ \t]+(\d{1,3}(?:,\d{1,3})?)[ \t]+(\d{1,3}(?:,\d{1,3})?)[ \t]+(\d{1,3}(?:,\d{1,3})?)/
|
125
|
+
|
126
|
+
if line =~ regex1
|
127
|
+
values = [$1.to_f, $2.to_f, $3.to_f]
|
128
|
+
puts " quotes: >#{values.join('|')}<"
|
129
|
+
|
130
|
+
line.sub!( regex1, ' [QUOTES.EN]' )
|
131
|
+
|
132
|
+
return values
|
133
|
+
elsif line =~ regex2
|
134
|
+
values = [$1.tr(',','.').to_f,
|
135
|
+
$2.tr(',','.').to_f,
|
136
|
+
$3.tr(',','.').to_f]
|
137
|
+
puts " quotes: >#{values.join('|')}<"
|
138
|
+
|
139
|
+
line.sub!( regex2, ' [QUOTES.DE]' )
|
140
|
+
|
141
|
+
return values
|
142
|
+
else
|
143
|
+
return nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def find_team_worker!( line, index )
|
149
|
+
regex = /@@oo([^@]+?)oo@@/ # e.g. everything in @@ .... @@ (use non-greedy +? plus all chars but not @, that is [^@])
|
150
|
+
|
151
|
+
if line =~ regex
|
152
|
+
value = "#{$1}"
|
153
|
+
puts " team#{index}: >#{value}<"
|
154
|
+
|
155
|
+
line.sub!( regex, "[TEAM#{index}]" )
|
156
|
+
|
157
|
+
return $1
|
158
|
+
else
|
159
|
+
return nil
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def find_team1!( line )
|
164
|
+
find_team_worker!( line, 1 )
|
165
|
+
end
|
166
|
+
|
167
|
+
def find_team2!( line )
|
168
|
+
find_team_worker!( line, 2 )
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
def match_team_worker!( line, key, values )
|
173
|
+
values.each do |value|
|
174
|
+
regex = Regexp.new( "\\b#{value}\\b" ) # wrap with world boundry (e.g. match only whole words e.g. not wac in wacker)
|
175
|
+
if line =~ regex
|
176
|
+
puts " match for team >#{key}< >#{value}<"
|
177
|
+
# make sure @@oo{key}oo@@ doesn't match itself with other key e.g. wacker, wac, etc.
|
178
|
+
line.sub!( regex, "@@oo#{key}oo@@" )
|
179
|
+
return true # break out after first match (do NOT continue)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
return false
|
183
|
+
end
|
184
|
+
|
185
|
+
def match_teams!( line )
|
186
|
+
@known_teams.each do |rec|
|
187
|
+
key = rec[0]
|
188
|
+
values = rec[1]
|
189
|
+
match_team_worker!( line, key, values )
|
190
|
+
end # each known_teams
|
191
|
+
end # method translate_teams!
|
192
|
+
|
193
|
+
|
194
|
+
def parse_quotes( data )
|
195
|
+
|
196
|
+
data.each_line do |line|
|
197
|
+
|
198
|
+
if line =~ /^\s*#/
|
199
|
+
# skip komments and do NOT copy to result (keep comments secret!)
|
200
|
+
logger.debug 'skipping comment line'
|
201
|
+
next
|
202
|
+
end
|
203
|
+
|
204
|
+
if line =~ /^\s*$/
|
205
|
+
# kommentar oder leerzeile überspringen
|
206
|
+
logger.debug 'skipping blank line'
|
207
|
+
next
|
208
|
+
end
|
209
|
+
|
210
|
+
# remove leading and trailing whitespace
|
211
|
+
line = line.strip
|
212
|
+
|
213
|
+
if is_round?( line )
|
214
|
+
puts "parsing round line: >#{line}<"
|
215
|
+
pos = find_round_pos!( line )
|
216
|
+
puts " line: >#{line}<"
|
217
|
+
|
218
|
+
@round = Round.find_by_event_id_and_pos!( @event.id, pos )
|
219
|
+
|
220
|
+
|
221
|
+
else
|
222
|
+
puts "parsing game (fixture) line: >#{line}<"
|
223
|
+
|
224
|
+
match_teams!( line )
|
225
|
+
team1_key = find_team1!( line )
|
226
|
+
team2_key = find_team2!( line )
|
227
|
+
|
228
|
+
quotes = find_quotes!( line )
|
229
|
+
|
230
|
+
puts " line: >#{line}<"
|
231
|
+
|
232
|
+
|
233
|
+
### todo: cache team lookups in hash?
|
234
|
+
|
235
|
+
team1 = Team.find_by_key!( team1_key )
|
236
|
+
team2 = Team.find_by_key!( team2_key )
|
237
|
+
|
238
|
+
### check if games exists
|
239
|
+
## with this teams in this round if yes only update
|
240
|
+
game = Game.find_by_round_id_and_team1_id_and_team2_id!(
|
241
|
+
@round.id, team1.id, team2.id
|
242
|
+
)
|
243
|
+
|
244
|
+
quote_attribs = {
|
245
|
+
odds1: quotes[0],
|
246
|
+
oddsx: quotes[1],
|
247
|
+
odds2: quotes[2]
|
248
|
+
}
|
249
|
+
|
250
|
+
quote = Quote.find_by_service_id_and_game_id( @service.id, game.id )
|
251
|
+
|
252
|
+
if quote.present?
|
253
|
+
puts "*** update quote #{quote.id}:"
|
254
|
+
else
|
255
|
+
puts "*** create quote:"
|
256
|
+
quote = Quote.new
|
257
|
+
|
258
|
+
more_quote_attribs = {
|
259
|
+
service_id: @service.id,
|
260
|
+
game_id: game.id
|
261
|
+
}
|
262
|
+
quote_attribs = quote_attribs.merge( more_quote_attribs )
|
263
|
+
end
|
264
|
+
|
265
|
+
puts quote_attribs.to_json
|
266
|
+
|
267
|
+
quote.update_attributes!( quote_attribs )
|
268
|
+
end
|
269
|
+
end # oldlines.each
|
270
|
+
|
271
|
+
end # method parse_quotes
|
272
|
+
|
273
|
+
end # class Reader
|
274
|
+
|
275
|
+
end # module SportDB::Market
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sportdb-market
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Gerald Bauer
|