sportdb-formats 1.2.0 → 2.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/CHANGELOG.md +1 -1
- data/Manifest.txt +6 -7
- data/Rakefile +7 -3
- data/lib/sportdb/formats/csv/goal.rb +192 -0
- data/lib/sportdb/formats/csv/goal_parser_csv.rb +28 -0
- data/lib/sportdb/formats/csv/match_parser_csv.rb +490 -0
- data/lib/sportdb/formats/csv/match_status_parser.rb +90 -0
- data/lib/sportdb/formats/match/conf_parser.rb +14 -2
- data/lib/sportdb/formats/match/match_parser.rb +502 -466
- data/lib/sportdb/formats/name_helper.rb +87 -0
- data/lib/sportdb/formats/search/sport.rb +69 -54
- data/lib/sportdb/formats/search/structs.rb +116 -0
- data/lib/sportdb/formats/search/world.rb +40 -22
- data/lib/sportdb/formats/version.rb +2 -2
- data/lib/sportdb/formats.rb +82 -15
- metadata +69 -14
- data/lib/sportdb/formats/goals.rb +0 -313
- data/lib/sportdb/formats/lines_reader.rb +0 -47
- data/lib/sportdb/formats/match/mapper.rb +0 -319
- data/lib/sportdb/formats/match/mapper_teams.rb +0 -23
- data/lib/sportdb/formats/match/match_parser_auto_conf.rb +0 -270
- data/lib/sportdb/formats/outline_reader.rb +0 -90
- data/lib/sportdb/formats/parser_helper.rb +0 -90
data/lib/sportdb/formats.rb
CHANGED
@@ -1,25 +1,51 @@
|
|
1
1
|
## 3rd party gems
|
2
2
|
require 'sportdb/structs'
|
3
|
+
require 'sportdb/parser'
|
4
|
+
require 'date/formats'
|
3
5
|
|
4
6
|
|
5
7
|
require 'zip' ## todo/check: if zip is alreay included in a required module
|
6
8
|
|
7
9
|
|
10
|
+
## note - add cocos (code commons)
|
11
|
+
##
|
12
|
+
## pulls in read_csv & parse_csv etc.
|
13
|
+
require 'cocos'
|
14
|
+
|
15
|
+
|
16
|
+
require 'logutils'
|
17
|
+
module SportDb
|
18
|
+
## logging machinery shortcut; use LogUtils for now
|
19
|
+
Logging = LogUtils::Logging
|
20
|
+
end
|
21
|
+
|
22
|
+
|
8
23
|
|
9
24
|
|
10
25
|
###
|
11
26
|
# our own code
|
12
27
|
require_relative 'formats/version' # let version always go first
|
13
28
|
|
14
|
-
require_relative 'formats/outline_reader'
|
15
29
|
require_relative 'formats/datafile'
|
16
30
|
require_relative 'formats/datafile_package'
|
17
31
|
require_relative 'formats/package'
|
18
32
|
|
33
|
+
require_relative 'formats/name_helper'
|
34
|
+
|
19
35
|
|
20
|
-
require_relative 'formats/parser_helper'
|
21
36
|
|
22
|
-
|
37
|
+
## let's put test configuration in its own namespace / module
|
38
|
+
module SportDb
|
39
|
+
class Test ## todo/check: works with module too? use a module - why? why not?
|
40
|
+
|
41
|
+
####
|
42
|
+
# todo/fix: find a better way to configure shared test datasets - why? why not?
|
43
|
+
# note: use one-up (..) directory for now as default - why? why not?
|
44
|
+
def self.data_dir() @data_dir ||= '../test'; end
|
45
|
+
def self.data_dir=( path ) @data_dir = path; end
|
46
|
+
end
|
47
|
+
end # module SportDb
|
48
|
+
|
23
49
|
|
24
50
|
|
25
51
|
###
|
@@ -29,13 +55,10 @@ module SportDb
|
|
29
55
|
module Import
|
30
56
|
|
31
57
|
class Configuration
|
32
|
-
## note: add more configs (open class), see sportdb-structs for original config!!!
|
33
|
-
|
34
|
-
## add
|
35
58
|
def world() @world ||= WorldSearch.new( countries: DummyCountrySearch.new ); end
|
36
59
|
def world=(world) @world = world; end
|
37
60
|
|
38
|
-
##
|
61
|
+
## todo/fix - add/move catalog here from sportdb-catalogs!!!
|
39
62
|
## def catalog() @catalog ||= Catalog.new; end
|
40
63
|
## def catalog(catalog) @catalog = catalog; end
|
41
64
|
end # class Configuration
|
@@ -43,11 +66,62 @@ end # class Configuration
|
|
43
66
|
## e.g. use config.catalog -- keep Import.catalog as a shortcut (for "read-only" access)
|
44
67
|
## def self.catalog() config.catalog; end
|
45
68
|
def self.world() config.world; end
|
69
|
+
|
70
|
+
## lets you use
|
71
|
+
## SportDb::Import.configure do |config|
|
72
|
+
## config.catalog_path = './catalog.db'
|
73
|
+
## end
|
74
|
+
def self.configure() yield( config ); end
|
75
|
+
def self.config() @config ||= Configuration.new; end
|
46
76
|
end # module Import
|
47
77
|
end # module SportDb
|
48
78
|
|
79
|
+
|
49
80
|
require_relative 'formats/search/world'
|
50
81
|
require_relative 'formats/search/sport'
|
82
|
+
require_relative 'formats/search/structs'
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
module Sports
|
87
|
+
## note: just forward to SportDb::Import configuration!!!!!
|
88
|
+
## keep Sports module / namespace "clean" - why? why not?
|
89
|
+
## that is, only include data structures (e.g. Match,League,etc) for now - why? why not?
|
90
|
+
def self.configure() yield( config ); end
|
91
|
+
def self.config() SportDb::Import.config; end
|
92
|
+
end # module Sports
|
93
|
+
|
94
|
+
|
95
|
+
###
|
96
|
+
# csv (tabular dataset) support / machinery
|
97
|
+
require_relative 'formats/csv/match_status_parser'
|
98
|
+
require_relative 'formats/csv/goal'
|
99
|
+
require_relative 'formats/csv/goal_parser_csv'
|
100
|
+
require_relative 'formats/csv/match_parser_csv'
|
101
|
+
|
102
|
+
|
103
|
+
### add convenience shortcut helpers
|
104
|
+
module Sports
|
105
|
+
class Match
|
106
|
+
def self.read_csv( path, headers: nil, filters: nil, converters: nil, sep: nil )
|
107
|
+
SportDb::CsvMatchParser.read( path,
|
108
|
+
headers: headers,
|
109
|
+
filters: filters,
|
110
|
+
converters: converters,
|
111
|
+
sep: sep )
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.parse_csv( txt, headers: nil, filters: nil, converters: nil, sep: nil )
|
115
|
+
SportDb::CsvMatchParser.parse( txt,
|
116
|
+
headers: headers,
|
117
|
+
filters: filters,
|
118
|
+
converters: converters,
|
119
|
+
sep: sep )
|
120
|
+
end
|
121
|
+
end # class Match
|
122
|
+
end # module Sports
|
123
|
+
|
124
|
+
|
51
125
|
|
52
126
|
|
53
127
|
|
@@ -77,7 +151,7 @@ module SportDb
|
|
77
151
|
|
78
152
|
Player = ::Sports::Player
|
79
153
|
|
80
|
-
|
154
|
+
|
81
155
|
class Team
|
82
156
|
## add convenience lookup helper / method for name by season for now
|
83
157
|
## use clubs history - for now kept separate from struct - why? why not?
|
@@ -91,14 +165,7 @@ module SportDb
|
|
91
165
|
end # module SportDb
|
92
166
|
|
93
167
|
|
94
|
-
require_relative 'formats/goals'
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
require_relative 'formats/match/mapper'
|
99
|
-
require_relative 'formats/match/mapper_teams'
|
100
168
|
require_relative 'formats/match/match_parser'
|
101
|
-
require_relative 'formats/match/match_parser_auto_conf'
|
102
169
|
require_relative 'formats/match/conf_parser'
|
103
170
|
|
104
171
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sportdb-formats
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sportdb-structs
|
@@ -16,28 +16,84 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.3.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.3.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sportdb-parser
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.2.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: date-formats
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.0.2
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.0.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: cocos
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.4.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.4.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: logutils
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.6.1
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.6.1
|
27
83
|
- !ruby/object:Gem::Dependency
|
28
84
|
name: rubyzip
|
29
85
|
requirement: !ruby/object:Gem::Requirement
|
30
86
|
requirements:
|
31
87
|
- - ">="
|
32
88
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
89
|
+
version: 2.3.2
|
34
90
|
type: :runtime
|
35
91
|
prerelease: false
|
36
92
|
version_requirements: !ruby/object:Gem::Requirement
|
37
93
|
requirements:
|
38
94
|
- - ">="
|
39
95
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
96
|
+
version: 2.3.2
|
41
97
|
- !ruby/object:Gem::Dependency
|
42
98
|
name: rdoc
|
43
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,23 +143,22 @@ files:
|
|
87
143
|
- Rakefile
|
88
144
|
- lib/sportdb/formats.rb
|
89
145
|
- lib/sportdb/formats/country/country_reader.rb
|
146
|
+
- lib/sportdb/formats/csv/goal.rb
|
147
|
+
- lib/sportdb/formats/csv/goal_parser_csv.rb
|
148
|
+
- lib/sportdb/formats/csv/match_parser_csv.rb
|
149
|
+
- lib/sportdb/formats/csv/match_status_parser.rb
|
90
150
|
- lib/sportdb/formats/datafile.rb
|
91
151
|
- lib/sportdb/formats/datafile_package.rb
|
92
152
|
- lib/sportdb/formats/event/event_reader.rb
|
93
|
-
- lib/sportdb/formats/goals.rb
|
94
153
|
- lib/sportdb/formats/ground/ground_reader.rb
|
95
154
|
- lib/sportdb/formats/league/league_outline_reader.rb
|
96
155
|
- lib/sportdb/formats/league/league_reader.rb
|
97
|
-
- lib/sportdb/formats/lines_reader.rb
|
98
156
|
- lib/sportdb/formats/match/conf_parser.rb
|
99
|
-
- lib/sportdb/formats/match/mapper.rb
|
100
|
-
- lib/sportdb/formats/match/mapper_teams.rb
|
101
157
|
- lib/sportdb/formats/match/match_parser.rb
|
102
|
-
- lib/sportdb/formats/
|
103
|
-
- lib/sportdb/formats/outline_reader.rb
|
158
|
+
- lib/sportdb/formats/name_helper.rb
|
104
159
|
- lib/sportdb/formats/package.rb
|
105
|
-
- lib/sportdb/formats/parser_helper.rb
|
106
160
|
- lib/sportdb/formats/search/sport.rb
|
161
|
+
- lib/sportdb/formats/search/structs.rb
|
107
162
|
- lib/sportdb/formats/search/world.rb
|
108
163
|
- lib/sportdb/formats/team/club_index_history.rb
|
109
164
|
- lib/sportdb/formats/team/club_reader.rb
|
@@ -125,7 +180,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
180
|
requirements:
|
126
181
|
- - ">="
|
127
182
|
- !ruby/object:Gem::Version
|
128
|
-
version:
|
183
|
+
version: 3.1.0
|
129
184
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
185
|
requirements:
|
131
186
|
- - ">="
|
@@ -1,313 +0,0 @@
|
|
1
|
-
|
2
|
-
module SportDb
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
class GoalsPlayerStruct
|
7
|
-
##
|
8
|
-
# note: player with own goal (o.g) gets listed on other team
|
9
|
-
# (thus, player might have two entries if also scored for its own team)
|
10
|
-
#
|
11
|
-
attr_accessor :name
|
12
|
-
attr_accessor :minutes # ary of minutes e.g. 30', 45+2', 72'
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
@minutes = []
|
16
|
-
end
|
17
|
-
|
18
|
-
def pretty_print( printer )
|
19
|
-
buf = String.new
|
20
|
-
buf << "<GoalsPlayerStruct: #{@name} "
|
21
|
-
buf << @minutes.pretty_print_inspect
|
22
|
-
buf << ">"
|
23
|
-
|
24
|
-
printer.text( buf )
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
class GoalsMinuteStruct
|
30
|
-
attr_accessor :minute, :offset
|
31
|
-
attr_accessor :penalty, :owngoal # flags
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
@offset = 0
|
35
|
-
@penalty = false
|
36
|
-
@owngoal = false
|
37
|
-
end
|
38
|
-
|
39
|
-
def pretty_print( printer )
|
40
|
-
buf = String.new
|
41
|
-
buf << "<GoalsMinuteStruct: #{@minute}"
|
42
|
-
buf << "+#{@offset}" if @offset && @offset > 0
|
43
|
-
buf << "'"
|
44
|
-
buf << " (o.g.)" if @owngoal
|
45
|
-
buf << " (pen.)" if @penalty
|
46
|
-
buf << ">"
|
47
|
-
|
48
|
-
printer.text( buf )
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
class GoalStruct
|
54
|
-
######
|
55
|
-
# flat struct for goals - one entry per goals
|
56
|
-
attr_accessor :name
|
57
|
-
attr_accessor :team # 1 or 2 ? check/todo: add team1 or team2 flag?
|
58
|
-
attr_accessor :minute, :offset
|
59
|
-
attr_accessor :penalty, :owngoal
|
60
|
-
attr_accessor :score1, :score2 # gets calculated
|
61
|
-
|
62
|
-
## add pos for sequence number? e.g. 1,2,3,4 (1st goald, 2nd goal, etc.) ???
|
63
|
-
|
64
|
-
|
65
|
-
def initialize( **kwargs ) ## add/allow quick and dirty quick init with keywords
|
66
|
-
if kwargs.empty?
|
67
|
-
# do nothing
|
68
|
-
else
|
69
|
-
kwargs.each do |key,value|
|
70
|
-
send( "#{key}=", value )
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def ==(o)
|
76
|
-
o.class == self.class && o.state == state
|
77
|
-
end
|
78
|
-
|
79
|
-
def state
|
80
|
-
[@name, @team, @minute, @offset, @penalty, @owngoal, @score1, @score2]
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
def pretty_print( printer )
|
85
|
-
buf = String.new
|
86
|
-
buf << "<GoalStruct: #{@score1}-#{@score2} #{@name} #{@minute}"
|
87
|
-
buf << "+#{@offset}" if @offset && @offset > 0
|
88
|
-
buf << "'"
|
89
|
-
buf << " (o.g.)" if @owngoal
|
90
|
-
buf << " (pen.)" if @penalty
|
91
|
-
buf << " for #{@team}" ### team 1 or 2 - use home/away
|
92
|
-
buf << ">"
|
93
|
-
|
94
|
-
printer.text( buf )
|
95
|
-
end
|
96
|
-
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
# todo: find a better name? to avoid confusing w/ GoalsParser? use MatchGoalsParser or similar?
|
103
|
-
class GoalsFinder
|
104
|
-
include LogUtils::Logging
|
105
|
-
|
106
|
-
|
107
|
-
def initialize
|
108
|
-
# nothing here for now
|
109
|
-
end
|
110
|
-
|
111
|
-
def find!( line, opts={} )
|
112
|
-
# remove end-of-line comments
|
113
|
-
line = line.sub( /#.*$/ ) do |_|
|
114
|
-
logger.debug " cutting off end of line comment - >>#{$&}<<"
|
115
|
-
''
|
116
|
-
end
|
117
|
-
|
118
|
-
# remove [] if presents e.g. [Neymar 12']
|
119
|
-
line = line.gsub( /[\[\]]/, '' )
|
120
|
-
# remove (single match) if line starts w/ - (allow spaces) e.g. [-;Neymar 12'] or [ - ;Neymar 12']
|
121
|
-
line = line.sub( /^[ ]*-[ ]*/, '' )
|
122
|
-
|
123
|
-
# split into left hand side (lhs) for team1 and
|
124
|
-
# right hand side (rhs) for team2
|
125
|
-
|
126
|
-
values = line.split( ';' )
|
127
|
-
|
128
|
-
# note: allow empty right hand side (e.g. team2 did NOT score any goals e.g. 3-0 etc.)
|
129
|
-
lhs = values[0]
|
130
|
-
rhs = values[1]
|
131
|
-
|
132
|
-
lhs = lhs.strip unless lhs.nil?
|
133
|
-
rhs = rhs.strip unless rhs.nil?
|
134
|
-
|
135
|
-
parser = GoalsParser.new
|
136
|
-
## todo/check: only call if not nil?
|
137
|
-
|
138
|
-
logger.debug " lhs (team1): >#{lhs}<"
|
139
|
-
lhs_data = parser.parse!( lhs )
|
140
|
-
pp lhs_data
|
141
|
-
|
142
|
-
logger.debug " rhs (team2): >#{rhs}<"
|
143
|
-
rhs_data = parser.parse!( rhs )
|
144
|
-
pp rhs_data
|
145
|
-
|
146
|
-
### merge into flat goal structs
|
147
|
-
goals = []
|
148
|
-
lhs_data.each do |player|
|
149
|
-
player.minutes.each do |minute|
|
150
|
-
goal = GoalStruct.new
|
151
|
-
goal.name = player.name
|
152
|
-
goal.team = 1
|
153
|
-
goal.minute = minute.minute
|
154
|
-
goal.offset = minute.offset
|
155
|
-
goal.penalty = minute.penalty
|
156
|
-
goal.owngoal = minute.owngoal
|
157
|
-
goals << goal
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
rhs_data.each do |player|
|
162
|
-
player.minutes.each do |minute|
|
163
|
-
goal = GoalStruct.new
|
164
|
-
goal.name = player.name
|
165
|
-
goal.team = 2
|
166
|
-
goal.minute = minute.minute
|
167
|
-
goal.offset = minute.offset
|
168
|
-
goal.penalty = minute.penalty
|
169
|
-
goal.owngoal = minute.owngoal
|
170
|
-
goals << goal
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
|
175
|
-
# sort by minute + offset
|
176
|
-
goals = goals.sort do |l,r|
|
177
|
-
res = l.minute <=> r.minute
|
178
|
-
if res == 0
|
179
|
-
res = l.offset <=> r.offset # pass 2: sort by offset
|
180
|
-
end
|
181
|
-
res
|
182
|
-
end
|
183
|
-
|
184
|
-
## calc score1,score2
|
185
|
-
score1 = 0
|
186
|
-
score2 = 0
|
187
|
-
goals.each do |goal|
|
188
|
-
if goal.team == 1
|
189
|
-
score1 += 1
|
190
|
-
elsif goal.team == 2
|
191
|
-
score2 += 1
|
192
|
-
else
|
193
|
-
# todo: should not happen: issue warning
|
194
|
-
end
|
195
|
-
goal.score1 = score1
|
196
|
-
goal.score2 = score2
|
197
|
-
end
|
198
|
-
|
199
|
-
logger.debug " #{goals.size} goals:"
|
200
|
-
pp goals
|
201
|
-
|
202
|
-
goals
|
203
|
-
end
|
204
|
-
|
205
|
-
end # class GoalsFinder
|
206
|
-
|
207
|
-
|
208
|
-
class GoalsParser
|
209
|
-
include LogUtils::Logging
|
210
|
-
|
211
|
-
|
212
|
-
# note: use ^ for start of string only!!!
|
213
|
-
# - for now slurp everything up to digits (inlc. spaces - use strip to remove)
|
214
|
-
# todo/check: use/rename to NAME_UNTIL_REGEX ??? ( add lookahead for spaces?)
|
215
|
-
NAME_REGEX = /^
|
216
|
-
[^0-9]+
|
217
|
-
/x
|
218
|
-
|
219
|
-
|
220
|
-
# todo/check: change to MINUTE_REGEX ??
|
221
|
-
# add MINUTE_SKIP_REGEX or MINUTE_SEP_REGEX /^[ ,]+/
|
222
|
-
# todo/fix: split out penalty and owngoal flag in PATTERN constant for reuse
|
223
|
-
MINUTES_REGEX = /^ # note: use ^ for start of string only!!!
|
224
|
-
(?<minute>[0-9]{1,3})
|
225
|
-
(?:\+
|
226
|
-
(?<offset>[1-9]{1})
|
227
|
-
)?
|
228
|
-
'
|
229
|
-
(?:[ ]*
|
230
|
-
\(
|
231
|
-
(?<type>P|pen\.|o\.g\.)
|
232
|
-
\)
|
233
|
-
)?
|
234
|
-
/x
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
def initialize
|
239
|
-
# nothing here for now
|
240
|
-
end
|
241
|
-
|
242
|
-
def parse!( line, opts={} )
|
243
|
-
|
244
|
-
## for now assume
|
245
|
-
## everything up-to 0-9 and , and () is part of player name
|
246
|
-
|
247
|
-
## try parsing lhs
|
248
|
-
## todo: check for empty - remove (make it same as empty string)
|
249
|
-
|
250
|
-
players = []
|
251
|
-
|
252
|
-
name = get_player_name!( line )
|
253
|
-
while name
|
254
|
-
logger.debug " found player name >#{name}< - remaining >#{line}<"
|
255
|
-
|
256
|
-
player = GoalsPlayerStruct.new
|
257
|
-
player.name = name
|
258
|
-
|
259
|
-
minute_hash = get_minute_hash!( line )
|
260
|
-
while minute_hash
|
261
|
-
logger.debug " found minutes >#{minute_hash.inspect}< - remaining >#{line}<"
|
262
|
-
|
263
|
-
minute = GoalsMinuteStruct.new
|
264
|
-
minute.minute = minute_hash[:minute].to_i
|
265
|
-
minute.offset = minute_hash[:offset].to_i if minute_hash[:offset]
|
266
|
-
if minute_hash[:type]
|
267
|
-
minute.owngoal = true if minute_hash[:type] =~ /o\.g\./
|
268
|
-
minute.penalty = true if minute_hash[:type] =~ /P|pen\./
|
269
|
-
end
|
270
|
-
player.minutes << minute
|
271
|
-
|
272
|
-
# remove commas and spaces (note: use ^ for start of string only!!!)
|
273
|
-
line.sub!( /^[ ,]+/, '' )
|
274
|
-
minute_hash = get_minute_hash!( line )
|
275
|
-
end
|
276
|
-
|
277
|
-
players << player
|
278
|
-
name = get_player_name!( line )
|
279
|
-
end
|
280
|
-
|
281
|
-
players
|
282
|
-
end # method parse!
|
283
|
-
|
284
|
-
private
|
285
|
-
def get_player_name!( line )
|
286
|
-
m = NAME_REGEX.match( line )
|
287
|
-
if m
|
288
|
-
## remove from line
|
289
|
-
line.slice!( 0...m[0].length )
|
290
|
-
m[0].strip # remove leading and trailing spaces
|
291
|
-
else
|
292
|
-
nil
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
def get_minute_hash!( line )
|
297
|
-
m = MINUTES_REGEX.match( line ) # note: use ^ for start of string only!!!
|
298
|
-
if m
|
299
|
-
h = {}
|
300
|
-
# - note: do NOT forget to turn name into symbol for lookup in new hash (name.to_sym)
|
301
|
-
m.names.each { |n| h[n.to_sym] = m[n] } # or use match_data.names.zip( match_data.captures ) - more cryptic but "elegant"??
|
302
|
-
|
303
|
-
## remove matched string from line
|
304
|
-
line.slice!( 0...m[0].length )
|
305
|
-
h
|
306
|
-
else
|
307
|
-
nil
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
end # class GoalsParser
|
312
|
-
|
313
|
-
end # module SportDb
|
@@ -1,47 +0,0 @@
|
|
1
|
-
|
2
|
-
module SportDb
|
3
|
-
|
4
|
-
class LinesReader ## change to LinesEnumerator - why? why not?
|
5
|
-
def initialize( lines )
|
6
|
-
@iter = lines.each ## get (external) enumerator (same as to_enum)
|
7
|
-
@lineno = 0
|
8
|
-
end
|
9
|
-
|
10
|
-
def each( &blk )
|
11
|
-
## note - StopIteration is rescued (automagically) by Kernel#loop.
|
12
|
-
## no need to rescue ourselves here
|
13
|
-
loop do
|
14
|
-
line = @iter.next ## note - raises StopIteration
|
15
|
-
blk.call( line )
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def each_with_index( &blk )
|
20
|
-
## note - StopIteration is rescued (automagically) by Kernel#loop.
|
21
|
-
loop do
|
22
|
-
line = @iter.next ## note - raises StopIteration
|
23
|
-
blk.call( line, @lineno )
|
24
|
-
@lineno += 1
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def peek
|
29
|
-
begin
|
30
|
-
@iter.peek
|
31
|
-
rescue StopIteration
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def next
|
37
|
-
## todo/check - do NOT catch StopIteration for next - why? why not?
|
38
|
-
begin
|
39
|
-
line = @iter.next
|
40
|
-
@lineno += 1
|
41
|
-
line
|
42
|
-
rescue StopIteration
|
43
|
-
nil
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end # class LinesReader
|
47
|
-
end # module SportDb
|