sportdb-quick 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ee93b658f2f6fce94a131931ea37908a2edbfa19a182ec20072447b57ffc847
4
- data.tar.gz: f5fa5ab664d71ea9a2ab8c62e1bd30d42fa678e0b2a7564124623dc5d7cd2963
3
+ metadata.gz: dc074126897237cd4cdb59c160e6dd41fa5b9e1ec3581472eeb68f520e486a61
4
+ data.tar.gz: 32112335ee74977ebd243e4ead540dabb49b291d837933315d57f7d3d86a47b4
5
5
  SHA512:
6
- metadata.gz: b0df0b970600ee24a88d09ddffec1caa8ef94cfa75561569638da954357cec2d97ffc7ea566f03e42ec814a5998c99da79f861c54a863a439f1778a3f0426ab0
7
- data.tar.gz: 01e1ad205c4d271a68136784a17b8f1b7987bdfa5e9b4ba5f395b42f6b488a53747525ecd7bea1e2c188012219529d7611e7d35f49ec4e5e7d499cc791e4e80a
6
+ metadata.gz: d0e23a4e07fa3e811c82eed4215d4229bc548e130d7e8bac70a85f8307928e50b28862e14b9a70c887166282c4efdb937d9533c1bfe0b1fddaced7b6b77d6729
7
+ data.tar.gz: dd442c2ae016aad05d5aa5e21dcd4420bb45808bdc18cf5759e97a37849527dcd1a9ab89da7a8ecb48a5d5a52956d7f36ae1b506a170dae138234e61925520a1
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.1.1
1
+ ### 0.2.0
2
2
 
3
3
  ### 0.0.1 / 2024-08-27
4
4
 
data/Manifest.txt CHANGED
@@ -3,13 +3,13 @@ Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
5
  bin/fbt
6
+ bin/fbtxt2json
6
7
  lib/sportdb/quick.rb
7
8
  lib/sportdb/quick/csv/goal.rb
8
9
  lib/sportdb/quick/csv/goal_parser_csv.rb
9
10
  lib/sportdb/quick/csv/match_parser_csv.rb
10
11
  lib/sportdb/quick/csv/match_status_parser.rb
11
12
  lib/sportdb/quick/match_parser.rb
12
- lib/sportdb/quick/opts.rb
13
13
  lib/sportdb/quick/quick_league_outline_reader.rb
14
14
  lib/sportdb/quick/quick_match_reader.rb
15
15
  lib/sportdb/quick/version.rb
data/bin/fbt CHANGED
@@ -26,8 +26,9 @@ require 'optparse'
26
26
 
27
27
  args = ARGV
28
28
  opts = { debug: false,
29
- metal: false,
30
- quick: true }
29
+ json: false,
30
+ file: nil,
31
+ }
31
32
 
32
33
  parser = OptionParser.new do |parser|
33
34
  parser.banner = "Usage: #{$PROGRAM_NAME} [options]"
@@ -35,24 +36,26 @@ require 'optparse'
35
36
  ##
36
37
  ## check if git has a offline option?? (use same)
37
38
  ## check for other tools - why? why not?
39
+ parser.on( "-q", "--quiet",
40
+ "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
41
+ opts[:debug] = false
42
+ end
43
+ # parser.on( "--verbose", "--debug",
44
+ # "turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
45
+ # opts[:debug] = true
46
+ # end
47
+
48
+ parser.on( "-j", "--json",
49
+ "print out in .json - default is (#{opts[:json]})" ) do |json|
50
+ opts[:json] = true
51
+ end
52
+
53
+ parser.on( "-f FILE", "--file FILE",
54
+ "read datafiles (pathspecs) via .csv file") do |file|
55
+ opts[:file] = file
56
+ end
38
57
 
39
58
 
40
- parser.on( "--verbose", "--debug",
41
- "turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
42
- opts[:debug] = true
43
- end
44
-
45
- parser.on( "--metal",
46
- "turn off typed parse tree; show to the metal tokens"+
47
- " (default: #{opts[:metal]})" ) do |metal|
48
- opts[:metal] = true
49
- end
50
-
51
- parser.on( "--quick",
52
- "use quick match reader; output matches in json"+
53
- " (default: #{opts[:quick]})" ) do |quick|
54
- opts[:quick] = true
55
- end
56
59
  end
57
60
  parser.parse!( args )
58
61
 
@@ -62,58 +65,117 @@ puts "ARGV:"
62
65
  p args
63
66
 
64
67
 
68
+ ## todo/check - use packs or projects or such
69
+ ## instead of specs - why? why not?
70
+ specs = []
71
+ if opts[:file]
72
+ recs = read_csv( opts[:file] )
73
+ pp recs
74
+ ## note - make pathspecs relative to passed in file arg!!!
75
+ basedir = File.dirname( opts[:file] )
76
+ recs.each do |rec|
77
+ paths = SportDb::Parser::Opts.find( rec['path'], dir: basedir )
78
+ specs << [paths, rec]
79
+ end
80
+ else
81
+ paths = if args.empty?
82
+ [
83
+ '../../../openfootball/euro/2021--europe/euro.txt',
84
+ '../../../openfootball/euro/2024--germany/euro.txt',
85
+ ]
86
+ else
87
+ ## check for directories
88
+ ## and auto-expand
89
+ SportDb::Parser::Opts.expand_args( args )
90
+ end
91
+ specs << [paths, {}]
92
+ end
93
+
94
+
95
+ if opts[:debug]
96
+ SportDb::QuickMatchReader.debug = true
97
+ SportDb::MatchParser.debug = true
98
+ else
99
+ SportDb::QuickMatchReader.debug = false
100
+ SportDb::MatchParser.debug = false
101
+ LogUtils::Logger.root.level = :info
102
+ end
65
103
 
66
104
 
105
+ specs.each_with_index do |(paths, rec),i|
106
+ errors = []
107
+ paths.each_with_index do |path,j|
108
+ puts "==> [#{j+1}/#{paths.size}] reading >#{path}<..."
109
+ quick = SportDb::QuickMatchReader.new( read_text( path ) )
110
+ matches = quick.parse
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
+
121
+ ## pp matches
122
+ ## try json for matches
123
+ if opts[:json]
124
+ data = matches.map {|match| match.as_json }
125
+ pp data
126
+ end
127
+ puts " #{matches.size} match(es)"
128
+ end
129
+
130
+ if errors.size > 0
131
+ puts
132
+ puts "!! #{errors.size} PARSE ERRORS in #{paths.size} datafile(s)"
133
+ pp errors
134
+ else
135
+ puts
136
+ puts " OK - no parse errors in #{paths.size} datafile(s)"
137
+ end
138
+
139
+ ## add errors to rec via rec['errors'] to allow
140
+ ## for further processing/reporting
141
+ rec['errors'] = errors
142
+ end
67
143
 
68
- paths = if args.empty?
69
- [
70
- '../../../openfootball/euro/2021--europe/euro.txt',
71
- '../../../openfootball/euro/2024--germany/euro.txt',
72
- ]
73
- else
74
- ## check for directories
75
- ## and auto-expand
76
-
77
- SportDb::Quick::Opts.expand_args( args )
78
- end
79
144
 
80
145
 
81
- if opts[:quick]
146
+ ###
147
+ ## generate a report if --file option used
148
+ if opts[:file]
82
149
 
83
- paths.each_with_index do |path,i|
84
- puts "==> [#{i+1}/#{paths.size}] reading >#{path}<..."
150
+ buf = String.new
85
151
 
86
- matches = SportDb::QuickMatchReader.read( path )
87
- ## pp matches
88
- ## try json for matches
89
- data = matches.map {|match| match.as_json }
90
- pp data
91
- puts
92
- puts " #{data.size} match(es)"
93
- end
94
- else
95
- SportDb::Parser::Linter.debug = true if opts[:debug]
152
+ buf << "# fbt summary report - #{specs.size} dataset(s)\n\n"
96
153
 
97
- linter = SportDb::Parser::Linter.new
154
+ specs.each_with_index do |(paths, rec),i|
155
+ errors = rec['errors']
98
156
 
99
- errors = []
157
+ if errors.size > 0
158
+ buf << "!! #{errors.size} ERROR(S) "
159
+ else
160
+ buf << " OK "
161
+ end
162
+ buf << "%-20s" % rec['path']
163
+ buf << " - #{paths.size} datafile(s)"
164
+ buf << "\n"
100
165
 
101
- paths.each_with_index do |path,i|
102
- puts "==> [#{i+1}/#{paths.size}] reading >#{path}<..."
103
- linter.read( path, parse: !opts[:metal] )
166
+ if errors.size > 0
167
+ buf << errors.pretty_inspect
168
+ buf << "\n"
169
+ end
170
+ end
104
171
 
105
- errors += linter.errors if linter.errors?
106
- end
172
+ puts
173
+ puts "SUMMARY:"
174
+ puts buf
107
175
 
108
- if errors.size > 0
109
- puts
110
- pp errors
111
- puts
112
- puts "!! #{errors.size} parse error(s) in #{paths.size} datafiles(s)"
113
- else
114
- puts
115
- puts "OK no parse errors found in #{paths.size} datafile(s)"
116
- end
176
+ # maybe write out in the future?
177
+ # basedir = File.dirname( opts[:file] )
178
+ # basename = File.basename( opts[:file], File.extname( opts[:file] ))
117
179
  end
118
180
 
119
181
 
data/bin/fbtxt2json ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## tip: to test run:
4
+ ## ruby -I ./lib bin/fbtxt2json
5
+
6
+
7
+ ## our own code
8
+ require 'sportdb/quick'
9
+
10
+
11
+
12
+ require 'optparse'
13
+
14
+
15
+
16
+ args = ARGV
17
+ opts = { debug: false,
18
+ }
19
+
20
+ parser = OptionParser.new do |parser|
21
+ parser.banner = "Usage: #{$PROGRAM_NAME} [options]"
22
+
23
+ ##
24
+ ## check if git has a offline option?? (use same)
25
+ ## check for other tools - why? why not?
26
+ parser.on( "-q", "--quiet",
27
+ "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
28
+ opts[:debug] = false
29
+ end
30
+ end
31
+ parser.parse!( args )
32
+
33
+ puts "OPTS:"
34
+ p opts
35
+ puts "ARGV:"
36
+ p args
37
+
38
+
39
+ path = if args.empty?
40
+ '../../../openfootball/euro/2021--europe/euro.txt'
41
+ elsif args.size == 1
42
+ args[0]
43
+ else
44
+ puts "!! wrong number of args, got #{args.size} - #{args.inspect} - only one arg supported/expected"
45
+ exit 1
46
+ end
47
+
48
+
49
+ if opts[:debug]
50
+ SportDb::QuickMatchReader.debug = true
51
+ SportDb::MatchParser.debug = true
52
+ else
53
+ SportDb::QuickMatchReader.debug = false
54
+ SportDb::MatchParser.debug = false
55
+ LogUtils::Logger.root.level = :info
56
+ end
57
+
58
+
59
+ puts "==> reading >#{path}<..."
60
+ quick = SportDb::QuickMatchReader.new( read_text( path ) )
61
+ matches = quick.parse
62
+
63
+ data = matches.map {|match| match.as_json }
64
+ pp data
65
+ puts
66
+ puts " #{matches.size} match(es)"
67
+
68
+ if quick.errors?
69
+ puts "!! #{quick.errors.size} parse error(s):"
70
+ pp quick.errors
71
+ end
72
+
73
+
74
+ puts "bye"
75
+
@@ -3,6 +3,12 @@ module SportDb
3
3
 
4
4
  class MatchParser ## simple match parser for team match schedules
5
5
 
6
+ def self.debug=(value) @@debug = value; end
7
+ def self.debug?() @@debug ||= false; end ## note: default is FALSE
8
+ def debug?() self.class.debug?; end
9
+
10
+ include Logging ## e.g. logger#debug, logger#info, etc.
11
+
6
12
 
7
13
  def self.parse( lines, start: )
8
14
  ## todo/fix: add support for txt and lines
@@ -13,11 +19,7 @@ class MatchParser ## simple match parser for team match schedules
13
19
  end
14
20
 
15
21
 
16
- include Logging ## e.g. logger#debug, logger#info, etc.
17
22
 
18
- def self.debug=(value) @@debug = value; end
19
- def self.debug?() @@debug ||= false; end ## note: default is FALSE
20
- def debug?() self.class.debug?; end
21
23
 
22
24
  def _read_lines( txt ) ## todo/check: add alias preproc_lines or build_lines or prep_lines etc. - why? why not?
23
25
  ## returns an array of lines with comments and empty lines striped / removed
@@ -70,10 +72,17 @@ class MatchParser ## simple match parser for team match schedules
70
72
  _read_lines( lines ) : lines
71
73
 
72
74
  @start = start
75
+ @errors = []
73
76
  end
74
77
 
75
78
 
79
+ attr_reader :errors
80
+ def errors?() @errors.size > 0; end
81
+
76
82
  def parse
83
+ ## note: every (new) read call - resets errors list to empty
84
+ @errors = []
85
+
77
86
  @last_date = nil
78
87
  @last_time = nil
79
88
  @last_round = nil
@@ -92,8 +101,6 @@ class MatchParser ## simple match parser for team match schedules
92
101
 
93
102
 
94
103
  @parser = Parser.new
95
-
96
- @errors = []
97
104
  @tree = []
98
105
 
99
106
  attrib_found = false
@@ -509,7 +516,7 @@ class MatchParser ## simple match parser for team match schedules
509
516
  end
510
517
  end
511
518
 
512
- pp [goals1,goals2]
519
+ pp [goals1,goals2] if debug?
513
520
 
514
521
  ## wrap in struct andd add/append to match
515
522
  =begin
@@ -547,7 +554,7 @@ class GoalStruct
547
554
  goals << goal
548
555
  end
549
556
 
550
- pp goals
557
+ pp goals if debug?
551
558
 
552
559
  ## quick & dirty - auto add goals to last match
553
560
  ## note - for hacky (quick& dirty) multi-line support
@@ -566,12 +573,13 @@ class GoalStruct
566
573
  logger.debug( "parse match: >#{nodes}<" )
567
574
 
568
575
  ## collect (possible) nodes by type
569
- num = nil
570
- date = nil
571
- time = nil
572
- teams = []
573
- score = nil
574
- more = []
576
+ num = nil
577
+ date = nil
578
+ time = nil
579
+ teams = []
580
+ score = nil
581
+ more = []
582
+ status = nil
575
583
 
576
584
  while !nodes.empty?
577
585
  node = nodes.shift
@@ -608,6 +616,8 @@ class GoalStruct
608
616
 
609
617
  score = Score.new( *values )
610
618
  ## pp score
619
+ elsif node_type == :status # e.g. awarded, canceled, postponed, etc.
620
+ status = node[1]
611
621
  elsif node_type == :vs
612
622
  ## skip; do nothing
613
623
  ##
@@ -626,7 +636,9 @@ class GoalStruct
626
636
  else
627
637
  puts "!! PARSE ERROR - unexpected node type #{node_type} in match line; got:"
628
638
  pp node
629
- exit 1
639
+ ## exit 1
640
+ @errors << ["PARSE ERROR - unexpected node type #{node_type} in match line; got: #{node.inspect}"]
641
+ return
630
642
  end
631
643
  end
632
644
 
@@ -634,7 +646,9 @@ class GoalStruct
634
646
  if teams.size != 2
635
647
  puts "!! PARSE ERROR - expected two teams; got #{teams.size}:"
636
648
  pp teams
637
- exit 1
649
+ ## exit 1
650
+ @errors << ["PARSE ERROR - expected two teams; got #{teams.size}: #{teams.inspect}"]
651
+ return
638
652
  end
639
653
 
640
654
  team1 = teams[0]
@@ -711,7 +725,6 @@ class GoalStruct
711
725
  time_str = time if date && time
712
726
 
713
727
 
714
- status = nil
715
728
  ground = nil
716
729
 
717
730
  @matches << Import::Match.new( num: num,
@@ -4,6 +4,12 @@
4
4
  module SportDb
5
5
  class QuickMatchReader
6
6
 
7
+ def self.debug=(value) @@debug = value; end
8
+ def self.debug?() @@debug ||= false; end ## note: default is FALSE
9
+ def debug?() self.class.debug?; end
10
+
11
+
12
+
7
13
  def self.read( path ) ## use - rename to read_file or from_file etc. - why? why not?
8
14
  txt = File.open( path, 'r:utf-8' ) {|f| f.read }
9
15
  parse( txt )
@@ -17,16 +23,23 @@ class QuickMatchReader
17
23
  include Logging
18
24
 
19
25
  def initialize( txt )
26
+ @errors = []
20
27
  @txt = txt
21
28
  end
22
29
 
30
+ attr_reader :errors
31
+ def errors?() @errors.size > 0; end
32
+
23
33
  def parse
34
+ ## note: every (new) read call - resets errors list to empty
35
+ @errors = []
36
+
24
37
  data = {} # return data hash with leagues
25
38
  # and seasons
26
39
  # for now merge stage into matches
27
40
 
28
41
  secs = QuickLeagueOutlineReader.parse( @txt )
29
- pp secs
42
+ pp secs if debug?
30
43
 
31
44
  secs.each do |sec| ## sec(tion)s
32
45
  season = Season.parse( sec[:season] ) ## convert (str) to season obj!!!
@@ -46,14 +59,20 @@ class QuickMatchReader
46
59
 
47
60
  auto_conf_teams, matches, rounds, groups = parser.parse
48
61
 
49
- puts ">>> #{auto_conf_teams.size} teams:"
50
- pp auto_conf_teams
51
- puts ">>> #{matches.size} matches:"
52
- ## pp matches
53
- puts ">>> #{rounds.size} rounds:"
54
- pp rounds
55
- puts ">>> #{groups.size} groups:"
56
- pp groups
62
+ ## auto-add "upstream" errors from parser
63
+ @errors += parser.errors if parser.errors?
64
+
65
+
66
+ if debug?
67
+ puts ">>> #{auto_conf_teams.size} teams:"
68
+ pp auto_conf_teams
69
+ puts ">>> #{matches.size} matches:"
70
+ ## pp matches
71
+ puts ">>> #{rounds.size} rounds:"
72
+ pp rounds
73
+ puts ">>> #{groups.size} groups:"
74
+ pp groups
75
+ end
57
76
 
58
77
  ## note: pass along stage (if present): stage - optional from heading!!!!
59
78
  if stage
@@ -3,8 +3,8 @@ module SportDb
3
3
  module Module
4
4
  module Quick
5
5
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
6
- MINOR = 1
7
- PATCH = 1
6
+ MINOR = 2
7
+ PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
data/lib/sportdb/quick.rb CHANGED
@@ -16,8 +16,8 @@ end
16
16
 
17
17
  ## our own code
18
18
  require_relative 'quick/version'
19
- require_relative 'quick/opts'
20
19
 
20
+ # require_relative 'quick/opts'
21
21
  # require_relative 'quick/linter'
22
22
  # require_relative 'quick/outline_reader'
23
23
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sportdb-quick
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.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-09-18 00:00:00.000000000 Z
11
+ date: 2024-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sportdb-parser
@@ -90,6 +90,7 @@ description: sportdb-quick - football.txt (quick) match readers and more
90
90
  email: gerald.bauer@gmail.com
91
91
  executables:
92
92
  - fbt
93
+ - fbtxt2json
93
94
  extensions: []
94
95
  extra_rdoc_files:
95
96
  - CHANGELOG.md
@@ -101,13 +102,13 @@ files:
101
102
  - README.md
102
103
  - Rakefile
103
104
  - bin/fbt
105
+ - bin/fbtxt2json
104
106
  - lib/sportdb/quick.rb
105
107
  - lib/sportdb/quick/csv/goal.rb
106
108
  - lib/sportdb/quick/csv/goal_parser_csv.rb
107
109
  - lib/sportdb/quick/csv/match_parser_csv.rb
108
110
  - lib/sportdb/quick/csv/match_status_parser.rb
109
111
  - lib/sportdb/quick/match_parser.rb
110
- - lib/sportdb/quick/opts.rb
111
112
  - lib/sportdb/quick/quick_league_outline_reader.rb
112
113
  - lib/sportdb/quick/quick_match_reader.rb
113
114
  - lib/sportdb/quick/version.rb
@@ -1,70 +0,0 @@
1
-
2
- module SportDb
3
- module Quick
4
-
5
- ###
6
- ## note - Opts Helpers for now nested inside Parser - keep here? why? why not?
7
- class Opts
8
-
9
- SEASON_RE = %r{ (?:
10
- \d{4}-\d{2}
11
- | \d{4}(--[a-z0-9_-]+)?
12
- )
13
- }x
14
- SEASON = SEASON_RE.source ## "inline" helper for embedding in other regexes - keep? why? why not?
15
-
16
-
17
- ## note: if pattern includes directory add here
18
- ## (otherwise move to more "generic" datafile) - why? why not?
19
- MATCH_RE = %r{ (?: ^|/ ) # beginning (^) or beginning of path (/)
20
- #{SEASON}
21
- /[a-z0-9_-]+\.txt$ ## txt e.g /1-premierleague.txt
22
- }x
23
-
24
-
25
- def self.find( path )
26
- datafiles = []
27
-
28
- ## note: normalize path - use File.expand_path ??
29
- ## change all backslash to slash for now
30
- ## path = path.gsub( "\\", '/' )
31
- path = File.expand_path( path )
32
-
33
- ## check all txt files
34
- ## note: incl. files starting with dot (.)) as candidates
35
- ## (normally excluded with just *)
36
- candidates = Dir.glob( "#{path}/**/{*,.*}.txt" )
37
- ## pp candidates
38
- candidates.each do |candidate|
39
- datafiles << candidate if MATCH_RE.match( candidate )
40
- end
41
-
42
- ## pp datafiles
43
- datafiles
44
- end
45
-
46
-
47
- def self.expand_args( args )
48
- paths = []
49
-
50
- args.each do |arg|
51
- ## check if directory
52
- if Dir.exist?( arg )
53
- datafiles = find( arg )
54
- puts
55
- puts " found #{datafiles.size} match txt datafiles in #{arg}"
56
- pp datafiles
57
- paths += datafiles
58
- else
59
- ## assume it's a file
60
- paths << arg
61
- end
62
- end
63
-
64
- paths
65
- end
66
- end # class Opts
67
-
68
-
69
- end # module Quick
70
- end # module SportDb