sportdb-formats 2.0.2 → 2.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +2 -20
- data/Rakefile +2 -7
- data/bin/fbchk +173 -0
- data/lib/sportdb/formats/quick_match_linter.rb +201 -0
- data/lib/sportdb/formats/version.rb +2 -2
- data/lib/sportdb/formats.rb +10 -269
- metadata +11 -85
- data/bin/fbx +0 -146
- data/lib/sportdb/formats/country/country_reader.rb +0 -142
- data/lib/sportdb/formats/csv/goal.rb +0 -192
- data/lib/sportdb/formats/csv/goal_parser_csv.rb +0 -28
- data/lib/sportdb/formats/csv/match_parser_csv.rb +0 -490
- data/lib/sportdb/formats/csv/match_status_parser.rb +0 -90
- data/lib/sportdb/formats/datafile.rb +0 -59
- data/lib/sportdb/formats/event/event_reader.rb +0 -119
- data/lib/sportdb/formats/ground/ground_reader.rb +0 -289
- data/lib/sportdb/formats/league/league_outline_reader.rb +0 -176
- data/lib/sportdb/formats/league/league_reader.rb +0 -152
- data/lib/sportdb/formats/match/conf_parser.rb +0 -132
- data/lib/sportdb/formats/match/match_parser.rb +0 -735
- data/lib/sportdb/formats/search/sport.rb +0 -372
- data/lib/sportdb/formats/search/structs.rb +0 -116
- data/lib/sportdb/formats/search/world.rb +0 -157
- data/lib/sportdb/formats/team/club_reader.rb +0 -318
- data/lib/sportdb/formats/team/club_reader_history.rb +0 -203
- data/lib/sportdb/formats/team/club_reader_props.rb +0 -90
- data/lib/sportdb/formats/team/wiki_reader.rb +0 -108
@@ -1,142 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
|
4
|
-
module SportDb
|
5
|
-
module Import
|
6
|
-
|
7
|
-
|
8
|
-
class CountryReader
|
9
|
-
|
10
|
-
|
11
|
-
def self.read( path ) ## use - rename to read_file or from_file etc. - why? why not?
|
12
|
-
txt = File.open( path, 'r:utf-8' ) { |f| f.read }
|
13
|
-
parse( txt )
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.parse( txt )
|
17
|
-
new( txt ).parse
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
def initialize( txt )
|
22
|
-
@txt = txt
|
23
|
-
end
|
24
|
-
|
25
|
-
def parse
|
26
|
-
countries = []
|
27
|
-
last_country = nil ## note/check/fix: use countries[-1] - why? why not?
|
28
|
-
|
29
|
-
OutlineReader.parse( @txt ).each do |node|
|
30
|
-
|
31
|
-
node_type = node[0]
|
32
|
-
|
33
|
-
if [:h1, :h2].include?( node_type )
|
34
|
-
## skip headings (and headings) for now too
|
35
|
-
elsif node_type == :p ## paragraph
|
36
|
-
lines = node[1]
|
37
|
-
lines.each do |line|
|
38
|
-
if line.start_with?( '|' )
|
39
|
-
## assume continuation with line of alternative names
|
40
|
-
## note: skip leading pipe
|
41
|
-
values = line[1..-1].split( '|' ) # team names - allow/use pipe(|)
|
42
|
-
## strip and squish (white)spaces
|
43
|
-
# e.g. East Germany (-1989) => East Germany (-1989)
|
44
|
-
values = values.map { |value| value.strip.gsub( /[ \t]+/, ' ' ) }
|
45
|
-
last_country.alt_names += values
|
46
|
-
elsif line =~ /^-[ ]*(\d{4})
|
47
|
-
[ ]+
|
48
|
-
(.+)$
|
49
|
-
/x ## check for historic lines e.g. -1989
|
50
|
-
year = $1.to_i
|
51
|
-
parts = $2.split( /=>|⇒/ )
|
52
|
-
values = parts[0].split( ',' )
|
53
|
-
values = values.map { |value| value.strip.gsub( /[ \t]+/, ' ' ) }
|
54
|
-
|
55
|
-
name = values[0]
|
56
|
-
code = values[1]
|
57
|
-
|
58
|
-
last_country = country = Country.new( name: "#{name} (-#{year})",
|
59
|
-
code: code )
|
60
|
-
## country.alt_names << name ## note: for now do NOT add name without year to alt_names - gets auto-add by index!!!
|
61
|
-
|
62
|
-
countries << country
|
63
|
-
## todo/fix: add reference to country today (in parts[1] !!!!)
|
64
|
-
else
|
65
|
-
## assume "regular" line
|
66
|
-
## check if starts with id (todo/check: use a more "strict"/better regex capture pattern!!!)
|
67
|
-
## note: allow country codes upto 4 (!!) e.g. Northern Cyprus
|
68
|
-
if line =~ /^([a-z]{2,4})
|
69
|
-
[ ]+
|
70
|
-
(.+)$/x
|
71
|
-
key = $1
|
72
|
-
values = $2.split( ',' )
|
73
|
-
## strip and squish (white)spaces
|
74
|
-
# e.g. East Germany (-1989) => East Germany (-1989)
|
75
|
-
values = values.map { |value| value.strip.gsub( /[ \t]+/, ' ' ) }
|
76
|
-
|
77
|
-
## note: remove "overlords" from geo-tree marked territories e.g. UK, US, etc. from name
|
78
|
-
## e.g. England › UK => England
|
79
|
-
## Puerto Rico › US => Puerto Rico
|
80
|
-
geos = split_geo( values[0] )
|
81
|
-
name = geos[0] ## note: ignore all other geos for now
|
82
|
-
|
83
|
-
## note: allow country codes up to 4 (!!) e.g. Northern Cyprus
|
84
|
-
code = if values[1] && values[1] =~ /^[A-Z]{3,4}$/ ## note: also check format
|
85
|
-
values[1]
|
86
|
-
else
|
87
|
-
if values[1]
|
88
|
-
puts "** !!! ERROR !!! wrong code format >#{values[1]}<; expected three (or four)-letter all up-case"
|
89
|
-
else
|
90
|
-
puts "** !!! ERROR !!! missing code for (canonical) country name"
|
91
|
-
end
|
92
|
-
exit 1
|
93
|
-
end
|
94
|
-
|
95
|
-
tags = if values[2] ## check if tags presents
|
96
|
-
split_tags( values[2] )
|
97
|
-
else
|
98
|
-
[]
|
99
|
-
end
|
100
|
-
|
101
|
-
last_country = country = Country.new( key: key,
|
102
|
-
name: name,
|
103
|
-
code: code,
|
104
|
-
tags: tags )
|
105
|
-
countries << country
|
106
|
-
else
|
107
|
-
puts "** !! ERROR - missing key for (canonical) country name"
|
108
|
-
exit 1
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end # each line
|
112
|
-
else
|
113
|
-
puts "** !! ERROR - unknown node type / (input) source line:"
|
114
|
-
pp node
|
115
|
-
exit 1
|
116
|
-
end
|
117
|
-
end # each node
|
118
|
-
|
119
|
-
countries
|
120
|
-
end # method parse
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
#######################################
|
125
|
-
## helpers
|
126
|
-
def split_tags( str )
|
127
|
-
tags = str.split( /[|<>‹›]/ ) ## allow pipe (|) and (<>‹›) as divider for now - add more? why? why not?
|
128
|
-
tags = tags.map { |tag| tag.strip }
|
129
|
-
tags
|
130
|
-
end
|
131
|
-
|
132
|
-
def split_geo( str ) ## todo/check: rename to parse_geo(s) - why? why not?
|
133
|
-
## split into geo tree
|
134
|
-
geos = str.split( /[<>‹›]/ ) ## note: allow > < or › ‹ for now
|
135
|
-
geos = geos.map { |geo| geo.strip } ## remove all whitespaces
|
136
|
-
geos
|
137
|
-
end
|
138
|
-
|
139
|
-
end # class CountryReader
|
140
|
-
|
141
|
-
end # module Import
|
142
|
-
end # module SportDb
|
@@ -1,192 +0,0 @@
|
|
1
|
-
|
2
|
-
module Sports
|
3
|
-
|
4
|
-
## "free-standing" goal event - for import/export in separate event / goal datafiles
|
5
|
-
## returned by CsvGoalParser and others
|
6
|
-
class GoalEvent
|
7
|
-
|
8
|
-
def self.build( row ) ## rename to parse or such - why? why not?
|
9
|
-
|
10
|
-
## split match_id
|
11
|
-
team_str, more_str = row['Match'].split( '|' )
|
12
|
-
team1_str, team2_str = team_str.split( ' - ' )
|
13
|
-
|
14
|
-
more_str = more_str.strip
|
15
|
-
team1_str = team1_str.strip
|
16
|
-
team2_str = team2_str.strip
|
17
|
-
|
18
|
-
# check if more_str is a date otherwise assume round
|
19
|
-
date_fmt = if more_str =~ /^[A-Z]{3} [0-9]{1,2}$/i ## Apr 4
|
20
|
-
'%b %d'
|
21
|
-
elsif more_str =~ /^[A-Z]{3} [0-9]{1,2} [0-9]{4}$/i ## Apr 4 2019
|
22
|
-
'%b %d %Y'
|
23
|
-
else
|
24
|
-
nil
|
25
|
-
end
|
26
|
-
|
27
|
-
if date_fmt
|
28
|
-
date = Date.strptime( more_str, date_fmt )
|
29
|
-
round = nil
|
30
|
-
else
|
31
|
-
date = nil
|
32
|
-
round = more_str
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
values = row['Score'].split('-')
|
37
|
-
values = values.map { |value| value.strip }
|
38
|
-
score1 = values[0].to_i
|
39
|
-
score2 = values[1].to_i
|
40
|
-
|
41
|
-
minute = nil
|
42
|
-
offset = nil
|
43
|
-
if m=%r{([0-9]+)
|
44
|
-
(?:[ ]+
|
45
|
-
\+([0-9]+)
|
46
|
-
)?
|
47
|
-
['.]
|
48
|
-
$}x.match( row['Minute'])
|
49
|
-
minute = m[1].to_i
|
50
|
-
offset = m[2] ? m[2].to_i : nil
|
51
|
-
else
|
52
|
-
puts "!! ERROR - unsupported minute (goal) format >#{row['Minute']}<"
|
53
|
-
exit 1
|
54
|
-
end
|
55
|
-
|
56
|
-
attributes = {
|
57
|
-
team1: team1_str,
|
58
|
-
team2: team2_str,
|
59
|
-
date: date,
|
60
|
-
round: round,
|
61
|
-
score1: score1,
|
62
|
-
score2: score2,
|
63
|
-
minute: minute,
|
64
|
-
offset: offset,
|
65
|
-
player: row['Player'],
|
66
|
-
owngoal: ['(og)', '(o.g.)'].include?( row['Extra']),
|
67
|
-
penalty: ['(pen)', '(pen.)'].include?( row['Extra']),
|
68
|
-
notes: (row['Notes'].nil? || row['Notes'].empty?) ? nil : row['Notes']
|
69
|
-
}
|
70
|
-
|
71
|
-
new( **attributes )
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
|
-
## match id
|
76
|
-
attr_reader :team1,
|
77
|
-
:team2,
|
78
|
-
:round, ## optional
|
79
|
-
:date ## optional
|
80
|
-
|
81
|
-
## main attributes
|
82
|
-
attr_reader :score1,
|
83
|
-
:score2,
|
84
|
-
:player,
|
85
|
-
:minute,
|
86
|
-
:offset,
|
87
|
-
:owngoal,
|
88
|
-
:penalty,
|
89
|
-
:notes
|
90
|
-
|
91
|
-
|
92
|
-
## todo/check: or just use match.hash or such if match mapping known - why? why not?
|
93
|
-
def match_id
|
94
|
-
if round
|
95
|
-
"#{@team1} - #{@team2} | #{@round}"
|
96
|
-
else
|
97
|
-
"#{@team1} - #{@team2} | #{@date}"
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
|
102
|
-
def owngoal?() @owngoal==true; end
|
103
|
-
def penalty?() @penalty==true; end
|
104
|
-
|
105
|
-
def initialize( team1:,
|
106
|
-
team2:,
|
107
|
-
round: nil,
|
108
|
-
date: nil,
|
109
|
-
score1:,
|
110
|
-
score2:,
|
111
|
-
player:,
|
112
|
-
minute:,
|
113
|
-
offset: nil,
|
114
|
-
owngoal: false,
|
115
|
-
penalty: false,
|
116
|
-
notes: nil
|
117
|
-
)
|
118
|
-
@team1 = team1
|
119
|
-
@team2 = team2
|
120
|
-
@round = round
|
121
|
-
@date = date
|
122
|
-
|
123
|
-
@score1 = score1
|
124
|
-
@score2 = score2
|
125
|
-
@player = player
|
126
|
-
@minute = minute
|
127
|
-
@offset = offset
|
128
|
-
@owngoal = owngoal
|
129
|
-
@penalty = penalty
|
130
|
-
@notes = notes
|
131
|
-
end
|
132
|
-
|
133
|
-
|
134
|
-
## note: lets you use normalize teams or such acts like a Match struct
|
135
|
-
def update( **kwargs )
|
136
|
-
## todo/fix: use team1_name, team2_name or similar - for compat with db activerecord version? why? why not?
|
137
|
-
@team1 = kwargs[:team1] if kwargs.has_key? :team1
|
138
|
-
@team2 = kwargs[:team2] if kwargs.has_key? :team2
|
139
|
-
end
|
140
|
-
end # class GoalEvent
|
141
|
-
|
142
|
-
|
143
|
-
### extend "basic" goal struct with goal event build
|
144
|
-
class Goal ### nested (non-freestanding) inside match (match is parent)
|
145
|
-
|
146
|
-
def self.build( events ) ## check/todo - rename to build_from_event/row or such - why? why not?
|
147
|
-
## build an array of goal structs from (csv) recs
|
148
|
-
recs = []
|
149
|
-
|
150
|
-
last_score1 = 0
|
151
|
-
last_score2 = 0
|
152
|
-
|
153
|
-
events.each do |event|
|
154
|
-
|
155
|
-
if last_score1+1 == event.score1 && last_score2 == event.score2
|
156
|
-
team = 1
|
157
|
-
elsif last_score2+1 == event.score2 && last_score1 == event.score1
|
158
|
-
team = 2
|
159
|
-
else
|
160
|
-
puts "!! ERROR - unexpected score advance (one goal at a time expected):"
|
161
|
-
puts " #{last_score1}-#{last_score2}=> #{event.score1}-#{event.score2}"
|
162
|
-
exit 1
|
163
|
-
end
|
164
|
-
|
165
|
-
last_score1 = event.score1
|
166
|
-
last_score2 = event.score2
|
167
|
-
|
168
|
-
|
169
|
-
attributes = {
|
170
|
-
score1: event.score1,
|
171
|
-
score2: event.score2,
|
172
|
-
team: team,
|
173
|
-
minute: event.minute,
|
174
|
-
offset: event.offset,
|
175
|
-
player: event.player,
|
176
|
-
owngoal: event.owngoal,
|
177
|
-
penalty: event.penalty,
|
178
|
-
notes: event.notes
|
179
|
-
}
|
180
|
-
|
181
|
-
recs << new( **attributes )
|
182
|
-
end
|
183
|
-
|
184
|
-
recs
|
185
|
-
end
|
186
|
-
end # class Goal
|
187
|
-
|
188
|
-
|
189
|
-
end # module Sports
|
190
|
-
|
191
|
-
|
192
|
-
|
@@ -1,28 +0,0 @@
|
|
1
|
-
|
2
|
-
module SportDb
|
3
|
-
class CsvGoalParser
|
4
|
-
|
5
|
-
|
6
|
-
def self.read( path )
|
7
|
-
txt = File.open( path, 'r:utf-8' ) {|f| f.read } ## note: make sure to use (assume) utf-8
|
8
|
-
parse( txt )
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.parse( txt )
|
12
|
-
new( txt ).parse
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
def initialize( txt )
|
17
|
-
@txt = txt
|
18
|
-
end
|
19
|
-
|
20
|
-
def parse
|
21
|
-
rows = parse_csv( @txt )
|
22
|
-
recs = rows.map { |row| Sports::GoalEvent.build( row ) }
|
23
|
-
## pp recs[0]
|
24
|
-
recs
|
25
|
-
end
|
26
|
-
|
27
|
-
end # class CsvGoalParser
|
28
|
-
end # module Sports
|