Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: a8ca3ef457
Fetching contributors…

Cannot retrieve contributors at this time

429 lines (303 sloc) 11.168 kb
module SportDB
class Reader
## make models available in sportdb module by default with namespace
# e.g. lets you use Team instead of Models::Team
include SportDB::Models
def initialize( logger=nil )
if logger.nil?
@logger = Logger.new(STDOUT)
@logger.level = Logger::INFO
else
@logger = logger
end
end
attr_reader :logger
def run( opts, args )
args.each do |arg|
name = arg # File.basename( arg, '.*' )
if opts.load?
load_fixtures_builtin( opts.event, name )
else
load_fixtures_with_include_path( opts.event, name, opts.data_path )
end
end
end
def load_fixtures_with_include_path( event_key, name, include_path ) # load from file system
path = "#{include_path}/#{name}.txt"
puts "*** parsing data '#{name}' (#{path})..."
code = File.read( path )
load_fixtures_worker( event_key, code )
end
def load_fixtures_builtin( event_key, name ) # load from gem (built-in)
path = "#{SportDB.root}/db/#{name}.txt"
puts "*** parsing data '#{name}' (#{path})..."
code = File.read( path )
load_fixtures_worker( event_key, code )
end
private
def load_fixtures_worker( event_key, data )
## assume active activerecord connection
##
@event = Event.find_by_key!( event_key )
puts "Event #{@event.key} >#{@event.title}<"
## build known teams table w/ synonyms e.g.
#
# nb: synonyms can be a regex not just a literal string
# [[ 'wolfsbrug', [ 'VfL Wolfsburg' ]],
# [ 'augsburg', [ 'FC Augsburg', 'Augi2', 'Augi3' ]],
# [ 'stuttgart', [ 'VfB Stuttgart' ]] ]
@known_teams = []
## todo/fix: move calc known_teams to model!!! (for reuse)
@event.teams.each_with_index do |team,index|
titles = []
titles << team.title
titles += team.synonyms.split('|') if team.synonyms.present?
## NB: sort here by length (largest goes first - best match)
# exclude code and key (key should always go last)
titles = titles.sort { |left,right| right.length <=> left.length }
titles << team.code if team.code.present?
titles << team.key
@known_teams << [ team.key, titles ]
puts " Team[#{index+1}] #{team.key} >#{titles.join('|')}<"
end
parse_fixtures( data )
end # method load_fixtures
def is_round?( line )
line =~ /Spieltag|Runde|Achtelfinale|Viertelfinale|Halbfinale|Finale/
end
def find_knockout_flag( line )
if line =~ /Achtelfinale|Viertelfinale|Halbfinale|Finale|K\.O\.|Knockout/
puts " setting knockout flag to true"
true
else
false
end
end
def find_round_pos!( line )
## fix/todo:
## if no round found assume last_pos+1 ??? why? why not?
regex = /\b(\d+)\b/
if line =~ regex
value = $1.to_i
puts " pos: >#{value}<"
line.sub!( regex, '[POS]' )
return value
else
return nil
end
end
def find_date!( line )
# extract date from line
# and return it
# NB: side effect - removes date from line string
# e.g. 2012-09-14 20:30 => YYYY-MM-DD HH:MM
regex_db = /\b(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})\b/
# e.g. 14.09. 20:30 => DD.MM. HH:MM
regex_de = /\b(\d{2})\.(\d{2})\.\s+(\d{2}):(\d{2})\b/
if line =~ regex_db
value = "#{$1}-#{$2}-#{$3} #{$4}:#{$5}"
puts " date: >#{value}<"
## todo: lets you configure year
## and time zone (e.g. cet, eet, utc, etc.)
line.sub!( regex_db, '[DATE.DB]' )
return DateTime.strptime( value, '%Y-%m-%d %H:%M' )
elsif line =~ regex_de
value = "2012-#{$2}-#{$1} #{$3}:#{$4}"
puts " date: >#{value}<"
## todo: lets you configure year
## and time zone (e.g. cet, eet, utc, etc.)
line.sub!( regex_de, '[DATE.DE]' )
return DateTime.strptime( value, '%Y-%m-%d %H:%M' )
else
return nil
end
end
def find_game_pos!( line )
# extract optional game pos from line
# and return it
# NB: side effect - removes pos from line string
# e.g. (1) - must start line
regex = /^[ \t]*\((\d{1,3})\)[ \t]+/
if line =~ regex
puts " pos: >#{$1}<"
line.sub!( regex, '[POS] ' )
return $1.to_i
else
return nil
end
end
def find_scores!( line )
# extract score from line
# and return it
# NB: side effect - removes date from line string
# e.g. 1:2 or 0:2 or 3:3
regex = /\b(\d):(\d)\b/
# e.g. 1:2nV => overtime
regex_ot = /\b(\d):(\d)[ \t]?[nN][vV]\b/
# e.g. 5:4iE => penalty
regex_p = /\b(\d):(\d)[ \t]?[iI][eE]\b/
scores = []
if line =~ regex
puts " score: >#{$1}-#{$2}<"
line.sub!( regex, '[SCORE]' )
scores << $1.to_i
scores << $2.to_i
if line =~ regex_ot
puts " score.ot: >#{$1}-#{$2}<"
line.sub!( regex_ot, '[SCORE.OT]' )
scores << $1.to_i
scores << $2.to_i
if line =~ regex_p
puts " score.p: >#{$1}-#{$2}<"
line.sub!( regex_p, '[SCORE.P]' )
scores << $1.to_i
scores << $2.to_i
end
end
end
scores
end # methdod find_scores!
def find_team_worker!( line, index )
regex = /@@oo([^@]+?)oo@@/ # e.g. everything in @@ .... @@ (use non-greedy +? plus all chars but not @, that is [^@])
if line =~ regex
value = "#{$1}"
puts " team#{index}: >#{value}<"
line.sub!( regex, "[TEAM#{index}]" )
return $1
else
return nil
end
end
def find_team1!( line )
find_team_worker!( line, 1 )
end
def find_team2!( line )
find_team_worker!( line, 2 )
end
def match_team_worker!( line, key, values )
values.each do |value|
regex = Regexp.new( "\\b#{value}\\b" ) # wrap with world boundry (e.g. match only whole words e.g. not wac in wacker)
if line =~ regex
puts " match for team >#{key}< >#{value}<"
# make sure @@oo{key}oo@@ doesn't match itself with other key e.g. wacker, wac, etc.
line.sub!( regex, "@@oo#{key}oo@@" )
return true # break out after first match (do NOT continue)
end
end
return false
end
def match_teams!( line )
@known_teams.each do |rec|
key = rec[0]
values = rec[1]
match_team_worker!( line, key, values )
end # each known_teams
end # method translate_teams!
def parse_fixtures( data )
data.each_line do |line|
if line =~ /^\s*#/
# skip komments and do NOT copy to result (keep comments secret!)
logger.debug 'skipping comment line'
next
end
if line =~ /^\s*$/
# kommentar oder leerzeile überspringen
logger.debug 'skipping blank line'
next
end
# remove leading and trailing whitespace
line = line.strip
if is_round?( line )
puts "parsing round line: >#{line}<"
pos = find_round_pos!( line )
@knockout_flag = find_knockout_flag( line )
puts " line: >#{line}<"
## NB: dummy/placeholder start_at, end_at date
## replace/patch after adding all games for round
round_attribs = {
title: "#{pos}. Runde"
}
@round = Round.find_by_event_id_and_pos( @event.id, pos )
if @round.present?
puts "*** update round #{@round.id}:"
else
puts "*** create round:"
@round = Round.new
round_attribs = round_attribs.merge( {
event_id: @event.id,
pos: pos,
start_at: Time.utc('1999-12-12'),
end_at: Time.utc('1999-12-12')
})
end
puts round_attribs.to_json
@round.update_attributes!( round_attribs )
### store list of round is for patching start_at/end_at at the end
@patch_rounds ||= {}
@patch_rounds[ @round.id ] = @round.id
else
puts "parsing game (fixture) line: >#{line}<"
pos = find_game_pos!( line )
match_teams!( line )
team1_key = find_team1!( line )
team2_key = find_team2!( line )
date = find_date!( line )
scores = find_scores!( line )
puts " line: >#{line}<"
### todo: cache team lookups in hash?
team1 = Team.find_by_key!( team1_key )
team2 = Team.find_by_key!( team2_key )
### check if games exists
## with this teams in this round if yes only update
game = Game.find_by_round_id_and_team1_id_and_team2_id(
@round.id, team1.id, team2.id
)
game_attribs = {
score1: scores[0],
score2: scores[1],
score3: scores[2],
score4: scores[3],
score5: scores[4],
score6: scores[5],
play_at: date,
knockout: @knockout_flag
}
game_attribs[ :pos ] = pos if pos.present?
if game.present?
puts "*** update game #{game.id}:"
else
puts "*** create game:"
game = Game.new
more_game_attribs = {
round_id: @round.id,
team1_id: team1.id,
team2_id: team2.id
}
## NB: use round.games.count for pos
## lets us add games out of order if later needed
more_game_attribs[ :pos ] = @round.games.count+1 if pos.nil?
game_attribs = game_attribs.merge( more_game_attribs )
end
puts game_attribs.to_json
game.update_attributes!( game_attribs )
end
end # oldlines.each
@patch_rounds ||= {}
@patch_rounds.each do |k,v|
puts "*** patch start_at/end_at date for round #{k}:"
round = Round.find( k )
games = round.games.order( 'play_at asc' ).all
## skip rouns w/ no games
## todo/fix: what's the best way for checking assoc w/ 0 recs?
next if games.size == 0
round_attribs = {}
## todo: check for no records
## e.g. if game[0].present? or just if game[0] ??
round_attribs[:start_at] = games[0].play_at
round_attribs[:end_at ] = games[-1].play_at
puts round_attribs.to_json
round.update_attributes!( round_attribs )
end
end # method parse_fixtures
end # class Reader
end # module SportDB
Jump to Line
Something went wrong with that request. Please try again.