fbtok 0.3.3 → 0.4.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.
@@ -0,0 +1,230 @@
1
+
2
+
3
+ module SportDb
4
+
5
+ class Pathspec
6
+
7
+ def self.debug=(value) @@debug = value; end
8
+ def self.debug?() @@debug ||= false; end ## note: default is FALSE
9
+
10
+
11
+ SEASON_RE = %r{ (?:
12
+ (?<season>\d{4}-\d{2})
13
+ | (?<season>\d{4})
14
+ )
15
+ }x
16
+
17
+ ## note: if pattern includes directory add here
18
+ ## (otherwise move to more "generic" datafile) - why? why not?
19
+ ## update - note include/allow dot (.) too
20
+ ## BUT NOT as first character!!! (e.g. exclude .confg.txt !!!)
21
+ ## e.g. 2024-25/at.1.txt
22
+ ## change to at_1 or uefa_cl or such - why? why not?
23
+ MATCH_RE = %r{
24
+ ## "classic" variant i) with season folder
25
+ ## e.g. /1930/cup.txt
26
+ (?:
27
+ (?: ^|/ ) # beginning (^) or beginning of path (/)
28
+
29
+ #{SEASON_RE}
30
+ (?: --[a-z0-9_-]+
31
+ )?
32
+ /
33
+ [a-z0-9][a-z0-9._-]* \.txt ## e.g /1-premierleague.txt
34
+ $
35
+ )
36
+
37
+ |
38
+ ## "compact" variant ii) with season in filename
39
+ ## e.g. 1930.txt or 2024_br.txt etc.
40
+ (?:
41
+ (?: ^|/ ) # beginning (^) or beginning of path (/)
42
+ #{SEASON_RE}
43
+ (?: _ ## allow more than one underscore - why? why not?
44
+ [a-z0-9][a-z0-9._-]*
45
+ )?
46
+ \.txt
47
+ $
48
+ )
49
+ }x
50
+
51
+ ### add support for matchdatafile with season NOT in directory
52
+ ## but starting filename e.g. 2024_friendlies.txt or 2024-25_bundesliga.txt
53
+
54
+
55
+ def self._norm_seasons( seasons )
56
+ seasons.map {|season| Season(season) }
57
+ end
58
+
59
+
60
+ ## todo/check - rename to glob or expand or such - why? why not?
61
+ def self._find( path, seasons: nil )
62
+ ## check - rename dir
63
+ ## use root_dir or work_dir or cd or such - why? why not?
64
+
65
+
66
+ ## note: normalize path - use File.expand_path ??
67
+ ## change all backslash to slash for now
68
+ ## path = path.gsub( "\\", '/' )
69
+ path = File.expand_path( path )
70
+
71
+ ####
72
+ ## note - make sure path exists; raise error if not
73
+ raise Errno::ENOENT, "No such directory - #{path})" unless Dir.exist?( path )
74
+
75
+
76
+ if seasons && seasons.size > 0
77
+ seasons = _norm_seasons( seasons ) ## norm seasons (string, integer => Season obj)
78
+ end
79
+
80
+
81
+ ## check all txt files
82
+ ## note: incl. files starting with dot (.)) as candidates
83
+ ## (normally excluded with just *)
84
+ ## was: Dir.glob( "#{path}/**/{*,.*}.txt" )
85
+
86
+ candidates = Dir.glob( "#{path}/**/*.txt" )
87
+ ## pp candidates
88
+
89
+
90
+ datafiles = []
91
+ candidates.each do |candidate|
92
+ if m = MATCH_RE.match( candidate )
93
+
94
+ ## check for seasons filter
95
+ next if seasons && seasons.size > 0 &&
96
+ !seasons.include?( Season.parse( m[:season] ))
97
+
98
+ ### exclude squad
99
+ ## and .v2 or .v2603
100
+ ##
101
+ ## - worldcup/more/1930_squads.txt => squads
102
+ ## - 2014--brazil/cup.v2.txt",
103
+ ## - 2014--brazil/cup.v260318_164934.txt",
104
+
105
+ basename = File.basename( candidate, File.extname( candidate ))
106
+
107
+ next if /squad/i.match( basename )
108
+ next if /\.v[0-9][0-9_]*/i.match( basename )
109
+
110
+
111
+ datafiles << candidate
112
+ end
113
+ end
114
+
115
+ ## pp datafiles
116
+ datafiles
117
+ end
118
+
119
+
120
+
121
+
122
+
123
+ def self.read( src )
124
+ recs = read_csv( src )
125
+ pp recs if debug?
126
+
127
+ ## note - make pathspecs relative to passed in file arg!!!
128
+ basedir = File.dirname( src )
129
+
130
+ recs.each do |rec|
131
+ path = rec['path']
132
+ fullpath = File.expand_path( path, basedir )
133
+ datafiles = _find( fullpath )
134
+
135
+ ## add (new) datafiles column (from expanded pathspec)
136
+ rec['datafiles'] = datafiles
137
+ end
138
+
139
+ recs
140
+ end
141
+
142
+
143
+
144
+
145
+ end # class Pathspec
146
+
147
+
148
+
149
+ ##
150
+ # PathspecReport (aka/formerly BatchReport)
151
+
152
+ class PathspecReport
153
+ def initialize( specs, title: )
154
+ @specs = specs
155
+ @title = title
156
+ end
157
+
158
+ def build
159
+ buf = String.new
160
+ buf << "# #{@title} - #{@specs.size} dataset(s)\n\n"
161
+
162
+ @specs.each_with_index do |rec,i|
163
+ datafiles = rec['datafiles']
164
+ errors = rec['errors']
165
+
166
+ if errors.size > 0
167
+ buf << "!! #{errors.size} ERROR(S) "
168
+ else
169
+ buf << " OK "
170
+ end
171
+ buf << "%-20s" % rec['path']
172
+ buf << " - #{datafiles.size} datafile(s)"
173
+ buf << "\n"
174
+
175
+ if errors.size > 0
176
+ buf << errors.pretty_inspect
177
+ buf << "\n"
178
+ end
179
+ end
180
+
181
+ buf
182
+ end # method build
183
+ end # class BatchReport
184
+
185
+ BatchReport = PathspecReport
186
+
187
+ end # module Sportdb
188
+
189
+
190
+
191
+ ##
192
+ ## keep helpers as global functions - why? why not?
193
+
194
+
195
+
196
+ def build_pathspecs( args ) ### note: was expand_args
197
+ specs = []
198
+
199
+ ## note - collect all "loose/standalone" files (NOT directories)
200
+ ## in single default pathspec node
201
+ more = []
202
+
203
+ args.each do |arg|
204
+ ## check if directory
205
+ if Dir.exist?( arg )
206
+ datafiles = SportDb::Pathspec._find( arg )
207
+ specs << { 'path' => arg,
208
+ 'datafiles' => datafiles }
209
+ elsif File.file?( arg ) ## assume it's a file
210
+ ## make sure path exists; raise error if not
211
+ ## (auto-)expand path to normalize - why? why not?
212
+ more << arg
213
+ else
214
+ raise Errno::ENOENT, "No such file or directory - #{arg}"
215
+ end
216
+ end
217
+
218
+ if more.size > 0
219
+ specs << { 'path' => '<input>',
220
+ 'datafiles' => more }
221
+ end
222
+
223
+ specs
224
+ end
225
+
226
+
227
+
228
+ def read_pathspecs( src )
229
+ SportDb::Pathspec.read( src )
230
+ end
data/lib/fbtok.rb CHANGED
@@ -1,13 +1,6 @@
1
1
 
2
- require 'sportdb/formats'
3
-
4
-
5
- ## more stdlibs
6
- require 'optparse' ## check - already auto-required in cocos? keep? why? why not?
2
+ require 'sportdb/quick'
7
3
 
8
4
 
9
5
  ## our own code
10
- require_relative 'fbtok/opts'
11
- require_relative 'fbtok/linter'
12
-
13
-
6
+ require_relative 'fbtok/pathspec'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fbtok
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.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: 2025-02-25 00:00:00.000000000 Z
11
+ date: 2026-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sportdb-parser
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.5.8
19
+ version: 0.7.1
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.5.8
26
+ version: 0.7.1
27
27
  - !ruby/object:Gem::Dependency
28
- name: sportdb-formats
28
+ name: sportdb-quick
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 2.1.2
33
+ version: 0.7.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 2.1.2
40
+ version: 0.7.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rdoc
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -75,8 +75,7 @@ dependencies:
75
75
  description: fbtok - football.txt lint tools incl. tokenizer, parser & more
76
76
  email: gerald.bauer@gmail.com
77
77
  executables:
78
- - fbchk
79
- - fbt
78
+ - fbquick
80
79
  - fbtok
81
80
  - fbtree
82
81
  - fbx
@@ -90,14 +89,12 @@ files:
90
89
  - Manifest.txt
91
90
  - README.md
92
91
  - Rakefile
93
- - bin/fbchk
94
- - bin/fbt
92
+ - bin/fbquick
95
93
  - bin/fbtok
96
94
  - bin/fbtree
97
95
  - bin/fbx
98
96
  - lib/fbtok.rb
99
- - lib/fbtok/linter.rb
100
- - lib/fbtok/opts.rb
97
+ - lib/fbtok/pathspec.rb
101
98
  homepage: https://github.com/sportdb/footty
102
99
  licenses:
103
100
  - Public Domain
data/bin/fbchk DELETED
@@ -1,158 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- ## tip: to test run:
4
- ## ruby -I ./lib bin/fbchk
5
-
6
-
7
- ## local hack for debugging
8
- ## use local versions if present
9
- $LOAD_PATH.unshift( File.expand_path( '/sports/sportdb/sport.db/sportdb-structs/lib' ))
10
- $LOAD_PATH.unshift( File.expand_path( '/sports/sportdb/sport.db/sportdb-catalogs/lib' ))
11
- $LOAD_PATH.unshift( File.expand_path( '/sports/sportdb/sport.db/sportdb-search/lib' ))
12
- $LOAD_PATH.unshift( File.expand_path( '/sports/sportdb/sport.db/sportdb-formats/lib' ))
13
-
14
-
15
- ## our own code
16
- require 'fbtok'
17
-
18
-
19
-
20
- ## local hack
21
- ## if exists up-to-date catalog db (use local version NOT built-in)
22
- catalog_path = '/sports/sportdb/sport.db/catalog/catalog.db'
23
- if File.exist?( catalog_path )
24
- SportDb::Import.config.catalog_path = catalog_path
25
- end
26
-
27
-
28
- args = ARGV
29
- opts = { debug: false,
30
- file: nil,
31
- teams: true, ## check/lint teams (name errros etc.)
32
- }
33
-
34
- parser = OptionParser.new do |parser|
35
- parser.banner = "Usage: #{$PROGRAM_NAME} [options]"
36
-
37
- ##
38
- ## check if git has a offline option?? (use same)
39
- ## check for other tools - why? why not?
40
- # parser.on( "-q", "--quiet",
41
- # "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
42
- # opts[:debug] = false
43
- # end
44
- parser.on( "--verbose", "--debug",
45
- "turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
46
- opts[:debug] = true
47
- end
48
-
49
- parser.on( "-f FILE", "--file FILE",
50
- "read datafiles (pathspecs) via .csv file") do |file|
51
- opts[:file] = file
52
- end
53
-
54
- parser.on( "--[no-]teams",
55
- "turn on/off team name checks (default: #{opts[:teams]})") do |teams|
56
- opts[:teams] = teams
57
- end
58
- end
59
- parser.parse!( args )
60
-
61
-
62
- if opts[:debug]
63
- puts "OPTS:"
64
- p opts
65
- puts "ARGV:"
66
- p args
67
-
68
- SportDb::QuickMatchLinter.debug = true
69
- SportDb::QuickMatchReader.debug = true
70
- SportDb::MatchParser.debug = true
71
- else
72
- SportDb::QuickMatchLinter.debug = false
73
- SportDb::QuickMatchReader.debug = false
74
- SportDb::MatchParser.debug = false
75
- LogUtils::Logger.root.level = :info
76
- end
77
-
78
-
79
-
80
-
81
- ## todo/check - use packs or projects or such
82
- ## instead of specs - why? why not?
83
- specs = if opts[:file]
84
- SportDb::Parser::Opts.read_pathspecs( opts[:file] )
85
- else
86
- paths = if args.empty?
87
- []
88
- else
89
- ## check for directories
90
- ## and auto-expand
91
- SportDb::Parser::Opts.expand_args( args )
92
- end
93
- ## always return array of specs
94
- [SportDb::Parser::Opts.build_pathspec( paths: paths)]
95
- end
96
-
97
-
98
-
99
-
100
- specs.each_with_index do |spec,i|
101
- paths = spec.paths
102
- rec = spec.rec
103
-
104
- errors = []
105
- paths.each_with_index do |path,j|
106
- puts "==> [#{i+1}/#{specs.size}, #{j+1}/#{paths.size}] reading >#{path}<..."
107
- quick = SportDb::QuickMatchLinter.new( read_text( path ),
108
- check_teams: opts[:teams] )
109
- matches = quick.parse
110
-
111
-
112
- if quick.errors?
113
- puts "!! #{quick.errors.size} error(s):"
114
- pp quick.errors
115
-
116
- quick.errors.each do |err|
117
- errors << [ path, *err ] # note: use splat (*) to add extra values (starting with msg)
118
- end
119
- end
120
- puts " #{matches.size} match(es)"
121
- end
122
-
123
- if errors.size > 0
124
- puts
125
- puts "!! #{errors.size} PARSE ERRORS in #{paths.size} datafile(s)"
126
- pp errors
127
- else
128
- puts
129
- puts " OK - no parse errors in #{paths.size} datafile(s)"
130
- end
131
-
132
- ## add errors to rec via rec['errors'] to allow
133
- ## for further processing/reporting
134
- rec['errors'] = errors
135
- end
136
-
137
-
138
-
139
- ###
140
- ## generate a report if --file option used
141
- if opts[:file]
142
- buf = SportDb::Parser::BatchReport.new(
143
- specs,
144
- title: 'fbchk summary report' ).build
145
-
146
- puts
147
- puts "SUMMARY:"
148
- puts buf
149
-
150
- basedir = File.dirname( opts[:file] )
151
- basename = File.basename( opts[:file], File.extname( opts[:file] ))
152
- outpath = "#{basedir}/fbchk.#{basename}.txt"
153
- write_text( outpath, buf )
154
- end
155
-
156
-
157
- puts "bye"
158
-
data/lib/fbtok/linter.rb DELETED
@@ -1,119 +0,0 @@
1
-
2
- module SportDb
3
- class Parser
4
-
5
- ###
6
- ## note - Linter for now nested inside Parser - keep? why? why not?
7
- class Linter
8
-
9
- def self.debug=(value) @@debug = value; end
10
- def self.debug?() @@debug ||= false; end ## note: default is FALSE
11
- def debug?() self.class.debug?; end
12
-
13
- ## turn warn(ings) into errors (default is false)
14
- def self.warn=(value) @@warn = value; end
15
- def self.warn?() @@warn ||= false; end ## note: default is FALSE
16
- def warn?() self.class.warn?; end
17
-
18
-
19
- attr_reader :errors
20
-
21
- def initialize
22
- @errors = []
23
- end
24
-
25
-
26
- def errors?() @errors.size > 0; end
27
-
28
-
29
-
30
- #########
31
- ## parse - false (default) - tokenize (only)
32
- ## - true - tokenize & parse
33
- def read( path, parse: true )
34
- ## note: every (new) read call - resets errors list to empty
35
- @errors = []
36
- @tree = []
37
-
38
- outline = QuickMatchOutline.read( path )
39
-
40
- outline.each_para do |lines|
41
-
42
- ## flatten lines (array of strings) into all-in-one string
43
- txt = lines.reduce( String.new ) do |mem, line|
44
- mem << line
45
- mem << "\n"
46
- mem
47
- end
48
-
49
- if parse
50
- if debug?
51
- puts "lines:"
52
- pp txt
53
- end
54
-
55
- ## pass along debug flag to parser (& tokenizer)?
56
- parser = RaccMatchParser.new( txt ) ## use own parser instance (not shared) - why? why not?
57
- tree, errors = parser.parse_with_errors
58
-
59
- if errors.size > 0
60
- ## add to "global" error list
61
- ## make a triplet tuple (file / msg / line text)
62
- errors.each do |msg|
63
- @errors << [path, msg]
64
- end
65
- end
66
-
67
- if debug?
68
- puts "parse tree:"
69
- pp tree
70
- end
71
-
72
- @tree += tree ## add nodes
73
-
74
- else ## process for tokenize only
75
-
76
- if debug?
77
- puts "lines:"
78
- pp txt
79
- end
80
-
81
- ##
82
- ## add (bakc) a line-by-line tracing (debug) option - why? why not?
83
- ## now debug output is by section (not line-by-line)
84
-
85
- lexer = Lexer.new( txt )
86
- t, errors = lexer.tokenize_with_errors
87
-
88
- if errors.size > 0
89
- ## add to "global" error list
90
- ## make a triplet tuple (file / msg / line text)
91
- errors.each do |msg|
92
- @errors << [path, msg]
93
- end
94
- end
95
-
96
- if debug?
97
- puts "tokens:"
98
- pp t
99
- end
100
-
101
- @tree += t ## add tokens to "tree"
102
- end # parse? (or tokenize?)
103
- end # each para (node)
104
-
105
-
106
- ##
107
- ## auto-add error if no tokens/tree nodes
108
- ## note - do NOT report empty file error by default
109
- if @tree.empty? && warn? ## @tree.size == 0
110
- @errors << [path, "empty; no #{parse ? 'parse tree nodes' : 'tokens'}"]
111
- end
112
-
113
- @tree ## return parse tree
114
- end # method read
115
- end # class Linter
116
-
117
-
118
- end # class Parser
119
- end # module SportDb