sportdb-formats 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 349708bb8a9fc26f5bc6ce47fd8b9b08b37f243e04e017fc22fa1057a165b733
4
- data.tar.gz: 9fb3ba56070b20d14aff9f8804c2201a0fb5599ff111fa8b454ddf32fe81e264
3
+ metadata.gz: c227a8566030829c53c10e3a8ab626b3222653ebcf795143e17c4627609a3e9f
4
+ data.tar.gz: 25cf88f75828207d783009f7f67f86de3cffab7265cc875fc893133678134bd5
5
5
  SHA512:
6
- metadata.gz: e3ffff589ba03e431c46d9244cb660572b94aa1f95ceb3d7d1b71182048d7c024bddeeaec6b43e36ddf0353071d4cb85b5db62f0a427677ce5e20a40cd347691
7
- data.tar.gz: 8a75b96b980429392dd9e909ba4f8441b96b129ec2cb6745eef65cf481889fb77ab426340ee277a28f6def7ae42e6de20f6d307aed082bea0df357d79d014ad0
6
+ metadata.gz: 5eb402eb00c0aff8e18a8079807cf39d1ed7d3e4d57786051d3737c39680958917a412a304fd40132b7aa1e7a7cc7d0d0a8e97f15307176e2fb54083e2f16caf
7
+ data.tar.gz: ba5db77e4b57f72ed3c305cd71bbc5ad7e150971ff9055544a4d3028ff3b24c579ec9c9915e651ed2eae1f2f4d19a7772f8aafc79af7f4d60dcc6e22e33bc930
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 2.0.1
1
+ ### 2.1.0
2
2
 
3
3
  ### 0.0.1 / 2019-10-28
4
4
 
data/Manifest.txt CHANGED
@@ -2,27 +2,10 @@ CHANGELOG.md
2
2
  Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
+ bin/fbchk
5
6
  lib/sportdb/formats.rb
6
- lib/sportdb/formats/country/country_reader.rb
7
- lib/sportdb/formats/csv/goal.rb
8
- lib/sportdb/formats/csv/goal_parser_csv.rb
9
- lib/sportdb/formats/csv/match_parser_csv.rb
10
- lib/sportdb/formats/csv/match_status_parser.rb
11
- lib/sportdb/formats/datafile.rb
12
7
  lib/sportdb/formats/datafile_package.rb
13
- lib/sportdb/formats/event/event_reader.rb
14
- lib/sportdb/formats/ground/ground_reader.rb
15
- lib/sportdb/formats/league/league_outline_reader.rb
16
- lib/sportdb/formats/league/league_reader.rb
17
- lib/sportdb/formats/match/conf_parser.rb
18
- lib/sportdb/formats/match/match_parser.rb
19
8
  lib/sportdb/formats/package.rb
20
- lib/sportdb/formats/search/sport.rb
21
- lib/sportdb/formats/search/structs.rb
22
- lib/sportdb/formats/search/world.rb
9
+ lib/sportdb/formats/quick_match_linter.rb
23
10
  lib/sportdb/formats/team/club_index_history.rb
24
- lib/sportdb/formats/team/club_reader.rb
25
- lib/sportdb/formats/team/club_reader_history.rb
26
- lib/sportdb/formats/team/club_reader_props.rb
27
- lib/sportdb/formats/team/wiki_reader.rb
28
11
  lib/sportdb/formats/version.rb
data/Rakefile CHANGED
@@ -20,13 +20,8 @@ Hoe.spec 'sportdb-formats' do
20
20
  self.licenses = ['Public Domain']
21
21
 
22
22
  self.extra_deps = [
23
- ['sportdb-structs', '>= 0.3.1'],
24
- ['sportdb-parser', '>= 0.2.0'],
25
- ['sportdb-catalogs', '>= 1.2.2'],
26
- ['date-formats', '>= 1.0.2'],
27
- ['cocos', '>= 0.4.0'],
28
- ['logutils', '>= 0.6.1'],
29
-
23
+ ['sportdb-search', '>= 0.0.1'],
24
+ ['sportdb-quick', '>= 0.2.0'],
30
25
  ['rubyzip', '>= 2.3.2' ],
31
26
  ]
32
27
 
data/bin/fbchk ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## tip: to test run:
4
+ ## ruby -I ./lib bin/fbchk
5
+
6
+ ## our own code
7
+ require 'sportdb/formats'
8
+
9
+
10
+
11
+ require 'optparse'
12
+
13
+
14
+
15
+ ## local hack
16
+ ## if exists up-to-date catalog db (use local version NOT built-in)
17
+ catalog_path = '../catalog/catalog.db'
18
+ if File.exist?( catalog_path )
19
+ SportDb::Import.config.catalog_path = catalog_path
20
+ end
21
+
22
+
23
+ args = ARGV
24
+ opts = { debug: false,
25
+ file: nil,
26
+ }
27
+
28
+ parser = OptionParser.new do |parser|
29
+ parser.banner = "Usage: #{$PROGRAM_NAME} [options]"
30
+
31
+ ##
32
+ ## check if git has a offline option?? (use same)
33
+ ## check for other tools - why? why not?
34
+ # parser.on( "-q", "--quiet",
35
+ # "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
36
+ # opts[:debug] = false
37
+ # end
38
+ parser.on( "--verbose", "--debug",
39
+ "turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
40
+ opts[:debug] = true
41
+ end
42
+
43
+ parser.on( "-f FILE", "--file FILE",
44
+ "read datafiles (pathspecs) via .csv file") do |file|
45
+ opts[:file] = file
46
+ end
47
+
48
+
49
+ end
50
+ parser.parse!( args )
51
+
52
+ puts "OPTS:"
53
+ p opts
54
+ puts "ARGV:"
55
+ p args
56
+
57
+
58
+ ## todo/check - use packs or projects or such
59
+ ## instead of specs - why? why not?
60
+ specs = []
61
+ if opts[:file]
62
+ recs = read_csv( opts[:file] )
63
+ pp recs
64
+ ## note - make pathspecs relative to passed in file arg!!!
65
+ basedir = File.dirname( opts[:file] )
66
+ recs.each do |rec|
67
+ paths = SportDb::Parser::Opts.find( rec['path'], dir: basedir )
68
+ specs << [paths, rec]
69
+ end
70
+ else
71
+ paths = if args.empty?
72
+ []
73
+ else
74
+ ## check for directories
75
+ ## and auto-expand
76
+ SportDb::Parser::Opts.expand_args( args )
77
+ end
78
+ specs << [paths, {}]
79
+ end
80
+
81
+
82
+ if opts[:debug]
83
+ SportDb::QuickMatchLinter.debug = true
84
+ SportDb::QuickMatchReader.debug = true
85
+ SportDb::MatchParser.debug = true
86
+ else
87
+ SportDb::QuickMatchLinter.debug = false
88
+ SportDb::QuickMatchReader.debug = false
89
+ SportDb::MatchParser.debug = false
90
+ LogUtils::Logger.root.level = :info
91
+ end
92
+
93
+
94
+ specs.each_with_index do |(paths, rec),i|
95
+ errors = []
96
+ paths.each_with_index do |path,j|
97
+ puts "==> [#{j+1}/#{paths.size}] reading >#{path}<..."
98
+ quick = SportDb::QuickMatchLinter.new( read_text( path ) )
99
+ matches = quick.parse
100
+
101
+ if quick.errors?
102
+ puts "!! #{quick.errors.size} error(s):"
103
+ pp quick.errors
104
+
105
+ quick.errors.each do |err|
106
+ errors << [ path, *err ] # note: use splat (*) to add extra values (starting with msg)
107
+ end
108
+ end
109
+ puts " #{matches.size} match(es)"
110
+ end
111
+
112
+ if errors.size > 0
113
+ puts
114
+ puts "!! #{errors.size} PARSE ERRORS in #{paths.size} datafile(s)"
115
+ pp errors
116
+ else
117
+ puts
118
+ puts " OK - no parse errors in #{paths.size} datafile(s)"
119
+ end
120
+
121
+ ## add errors to rec via rec['errors'] to allow
122
+ ## for further processing/reporting
123
+ rec['errors'] = errors
124
+ end
125
+
126
+
127
+
128
+ ###
129
+ ## generate a report if --file option used
130
+ if opts[:file]
131
+
132
+ buf = String.new
133
+
134
+ buf << "# fbchk summary report - #{specs.size} dataset(s)\n\n"
135
+
136
+ specs.each_with_index do |(paths, rec),i|
137
+ errors = rec['errors']
138
+
139
+ if errors.size > 0
140
+ buf << "!! #{errors.size} ERROR(S) "
141
+ else
142
+ buf << " OK "
143
+ end
144
+ buf << "%-20s" % rec['path']
145
+ buf << " - #{paths.size} datafile(s)"
146
+ buf << "\n"
147
+
148
+ if errors.size > 0
149
+ buf << errors.pretty_inspect
150
+ buf << "\n"
151
+ end
152
+ end
153
+
154
+ puts
155
+ puts "SUMMARY:"
156
+ puts buf
157
+
158
+ basedir = File.dirname( opts[:file] )
159
+ basename = File.basename( opts[:file], File.extname( opts[:file] ))
160
+ outpath = "#{basedir}/fbcheck.#{basename}.txt"
161
+ write_text( outpath, buf )
162
+ end
163
+
164
+
165
+ puts "bye"
166
+
@@ -0,0 +1,195 @@
1
+ #####
2
+ ## quick match linter for datafiles with league outlines
3
+
4
+
5
+ module SportDb
6
+ class QuickMatchLinter
7
+
8
+ def self.debug=(value) @@debug = value; end
9
+ def self.debug?() @@debug ||= false; end ## note: default is FALSE
10
+ def debug?() self.class.debug?; end
11
+
12
+
13
+
14
+ def self.read( path ) ## use - rename to read_file or from_file etc. - why? why not?
15
+ txt = File.open( path, 'r:utf-8' ) {|f| f.read }
16
+ parse( txt )
17
+ end
18
+
19
+ def self.parse( txt )
20
+ new( txt ).parse
21
+ end
22
+
23
+
24
+ include Logging
25
+
26
+ def initialize( txt )
27
+ @errors = []
28
+ @txt = txt
29
+ end
30
+
31
+ attr_reader :errors
32
+ def errors?() @errors.size > 0; end
33
+
34
+
35
+
36
+ CLUB_NAME_RE = %r{^
37
+ (?<name>[^()]+?) ## non-greedy
38
+ (?:
39
+ \s+
40
+ \(
41
+ (?<code>[A-Z][A-Za-z]{2,3}) ## optional (country) code; support single code e.g. (A) - why? why not?
42
+ \)
43
+ )?
44
+ $}x ## note - allow (URU) and (Uru) - why? why not
45
+
46
+
47
+ def parse
48
+ ## note: every (new) read call - resets errors list to empty
49
+ @errors = []
50
+
51
+ data = {} # return data hash with leagues
52
+ # and seasons
53
+ # for now merge stage into matches
54
+
55
+ secs = QuickLeagueOutlineReader.parse( @txt )
56
+ pp secs if debug?
57
+
58
+ secs.each do |sec| ## sec(tion)s
59
+ season = Season.parse( sec[:season] ) ## convert (str) to season obj!!!
60
+ league = sec[:league]
61
+ stage = sec[:stage]
62
+ lines = sec[:lines]
63
+
64
+ start = if season.year?
65
+ Date.new( season.start_year, 1, 1 )
66
+ else
67
+ Date.new( season.start_year, 7, 1 )
68
+ end
69
+
70
+ ###
71
+ ## db check - check league
72
+ ## fix/fix - add season to match_by !!!!!!!
73
+ recs = Import::League.match_by( name: league )
74
+ league_rec = nil
75
+ if recs.size == 1
76
+ league_rec = recs[0]
77
+ puts " OK #{league} => #{league_rec.name}"
78
+ elsif recs.size == 0
79
+ msg = "NAME ERROR - no league match found for >#{league}<"
80
+ @errors << [msg]
81
+ puts "!! #{msg}"
82
+ else
83
+ msg = "NAME ERROR - ambigous; too many league matches (#{recs.size}) found for >#{league}<"
84
+ @errors << [msg]
85
+ puts "!! #{msg}"
86
+ end
87
+
88
+
89
+ parser = MatchParser.new( lines,
90
+ start ) ## note: keep season start_at date for now (no need for more specific stage date need for now)
91
+
92
+ auto_conf_teams, matches, rounds, groups = parser.parse
93
+
94
+ ## auto-add "upstream" errors from parser
95
+ @errors += parser.errors if parser.errors?
96
+
97
+
98
+ if debug?
99
+ puts ">>> #{auto_conf_teams.size} teams:"
100
+ pp auto_conf_teams
101
+ puts ">>> #{matches.size} matches:"
102
+ ## pp matches
103
+ puts ">>> #{rounds.size} rounds:"
104
+ pp rounds
105
+ puts ">>> #{groups.size} groups:"
106
+ pp groups
107
+ end
108
+
109
+ ###
110
+ ## db check - check teams
111
+ ## only clubs for now
112
+ ## fix add better support for champs etc
113
+ ## and national teams!!!
114
+ auto_conf_teams.each do |team|
115
+ recs = if league_rec && !league_rec.intl?
116
+ Import::Club.match_by( name: team, league: league_rec )
117
+ else
118
+ ###
119
+ ## get country code from name
120
+ ## e.g. Liverpool FC (ENG) or
121
+ ## Liverpool FC (URU) etc.
122
+
123
+ ## check for country code
124
+ if m=CLUB_NAME_RE.match( team )
125
+ if m[:code]
126
+ Import::Club.match_by( name: m[:name],
127
+ country: m[:code] )
128
+ else
129
+ msg = "PARSE ERROR - country code missing for club name in int'l tournament; may not be unique >#{team}<"
130
+ @errors << [msg]
131
+ Import::Club.match_by( name: team )
132
+ end
133
+ else
134
+ msg = "PARSE ERROR - invalid club name; cannot match with CLUB_NAME_RE >#{team}<"
135
+ @errors << [msg]
136
+ []
137
+ end
138
+ end
139
+ team_rec = nil
140
+ if recs.size == 1
141
+ team_rec = recs[0]
142
+ puts " OK #{team} => #{team_rec.name}, #{team_rec.country.name}"
143
+ elsif recs.size == 0
144
+ msg = "NAME ERROR - no team match found for >#{team}<"
145
+ @errors << [msg]
146
+ puts "!! #{msg}"
147
+ else
148
+ msg = "NAME ERROR - ambigous; too many team matches (#{recs.size}) found for >#{team}<"
149
+ @errors << [msg]
150
+ puts "!! #{msg}"
151
+ end
152
+ end
153
+
154
+
155
+
156
+
157
+
158
+ ## note: pass along stage (if present): stage - optional from heading!!!!
159
+ if stage
160
+ matches.each do |match|
161
+ match = match.update( stage: stage )
162
+ end
163
+ end
164
+
165
+ data[ league ] ||= {}
166
+ data[ league ][ season.key ] ||= []
167
+
168
+ data[ league ][ season.key ] += matches
169
+ ## note - skip teams, rounds, and groups for now
170
+ end
171
+
172
+ ## check - only one league and one season
173
+ ## allowed in quick style
174
+
175
+
176
+ leagues = data.keys
177
+ if leagues.size != 1
178
+ puts "!! (QUICK) PARSE ERROR - expected one league only; got #{leagues.size}:"
179
+ pp leagues
180
+ exit 1
181
+ end
182
+
183
+ seasons = data[ leagues[0] ].keys
184
+ if seasons.size != 1
185
+ puts "!! (QUICK) PARSE ERROR - expected one #{leagues[0]} season only; got #{seasons.size}:"
186
+ pp seasons
187
+ exit 1
188
+ end
189
+
190
+ data[ leagues[0] ][ seasons[0] ]
191
+ end # method parse
192
+
193
+ end # class QuickMatchLinter
194
+ end # module SportDb
195
+
@@ -3,8 +3,8 @@ module Module
3
3
  module Formats
4
4
 
5
5
  MAJOR = 2 ## todo: namespace inside version or something - why? why not??
6
- MINOR = 0
7
- PATCH = 1
6
+ MINOR = 1
7
+ PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version