sportdb-formats 1.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +498 -489
- data/lib/sportdb/formats/name_helper.rb +87 -0
- data/lib/sportdb/formats/search/sport.rb +67 -52
- data/lib/sportdb/formats/search/structs.rb +116 -0
- data/lib/sportdb/formats/search/world.rb +40 -22
- data/lib/sportdb/formats/version.rb +3 -3
- data/lib/sportdb/formats.rb +82 -15
- metadata +69 -14
- data/lib/sportdb/formats/goals.rb +0 -321
- 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,321 +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
|
-
### todo/fix:
|
213
|
-
## let's use stringscanner for parsing line - why? why not?
|
214
|
-
|
215
|
-
|
216
|
-
# note: use ^ for start of string only!!!
|
217
|
-
# - for now slurp everything up to digits (inlc. spaces - use strip to remove)
|
218
|
-
# todo/check: use/rename to NAME_UNTIL_REGEX ??? ( add lookahead for spaces?)
|
219
|
-
NAME_REGEX = /^
|
220
|
-
[^0-9]+
|
221
|
-
/x
|
222
|
-
|
223
|
-
|
224
|
-
# todo/check: change to MINUTE_REGEX ??
|
225
|
-
# add MINUTE_SKIP_REGEX or MINUTE_SEP_REGEX /^[ ,]+/
|
226
|
-
# todo/fix: split out penalty and owngoal flag in PATTERN constant for reuse
|
227
|
-
# note - offset 90+10 possible!!!!
|
228
|
-
# note - allow p/pen./pen or o.g. or og
|
229
|
-
MINUTES_REGEX = /^ # note: use ^ for start of string only!!!
|
230
|
-
(?<minute>[0-9]{1,3})
|
231
|
-
(?:\+
|
232
|
-
(?<offset>[0-9]{1,2})
|
233
|
-
)?
|
234
|
-
'
|
235
|
-
(?:[ ]*
|
236
|
-
\(
|
237
|
-
(?<type>p|pen\.?|
|
238
|
-
og|o\.g\.)
|
239
|
-
\)
|
240
|
-
)?
|
241
|
-
/ix
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
def initialize
|
246
|
-
# nothing here for now
|
247
|
-
end
|
248
|
-
|
249
|
-
def parse!( line, opts={} )
|
250
|
-
|
251
|
-
## for now assume
|
252
|
-
## everything up-to 0-9 and , and () is part of player name
|
253
|
-
|
254
|
-
## try parsing lhs
|
255
|
-
## todo: check for empty - remove (make it same as empty string)
|
256
|
-
|
257
|
-
players = []
|
258
|
-
|
259
|
-
name = get_player_name!( line )
|
260
|
-
while name
|
261
|
-
logger.debug " found player name >#{name}< - remaining >#{line}<"
|
262
|
-
|
263
|
-
player = GoalsPlayerStruct.new
|
264
|
-
player.name = name
|
265
|
-
|
266
|
-
minute_hash = nil
|
267
|
-
while minute_hash=get_minute_hash!( line ) ## note: returns nil if no (regex) match
|
268
|
-
logger.debug " found minutes >#{minute_hash.inspect}< - remaining >#{line}<"
|
269
|
-
|
270
|
-
minute = GoalsMinuteStruct.new
|
271
|
-
minute.minute = minute_hash[:minute].to_i
|
272
|
-
minute.offset = minute_hash[:offset].to_i if minute_hash[:offset]
|
273
|
-
if minute_hash[:type]
|
274
|
-
minute.owngoal = true if minute_hash[:type] =~ /og|o\.g\./i
|
275
|
-
minute.penalty = true if minute_hash[:type] =~ /p|pen\.?/i
|
276
|
-
end
|
277
|
-
player.minutes << minute
|
278
|
-
|
279
|
-
# remove commas and spaces (note: use ^ for start of string only!!!)
|
280
|
-
line.sub!( /^[ ,]+/, '' )
|
281
|
-
end
|
282
|
-
|
283
|
-
players << player
|
284
|
-
name = get_player_name!( line )
|
285
|
-
end
|
286
|
-
|
287
|
-
players
|
288
|
-
end # method parse!
|
289
|
-
|
290
|
-
private
|
291
|
-
def get_player_name!( line )
|
292
|
-
m = NAME_REGEX.match( line )
|
293
|
-
if m
|
294
|
-
## remove from line
|
295
|
-
line.slice!( 0...m[0].length )
|
296
|
-
m[0].strip # remove leading and trailing spaces
|
297
|
-
else
|
298
|
-
nil
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
def get_minute_hash!( line )
|
303
|
-
m = MINUTES_REGEX.match( line ) # note: use ^ for start of string only!!!
|
304
|
-
if m
|
305
|
-
h = {}
|
306
|
-
## todo/fix - hash conversion no longer need in ruby 3+!!
|
307
|
-
## double check - and remove (simplify) !!!!
|
308
|
-
# - note: do NOT forget to turn name into symbol for lookup in new hash (name.to_sym)
|
309
|
-
m.names.each { |n| h[n.to_sym] = m[n] } # or use match_data.names.zip( match_data.captures ) - more cryptic but "elegant"??
|
310
|
-
|
311
|
-
## remove matched string from line
|
312
|
-
line.slice!( 0...m[0].length )
|
313
|
-
h
|
314
|
-
else
|
315
|
-
nil
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
end # class GoalsParser
|
320
|
-
|
321
|
-
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
|