sportdb-writers 0.2.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3483a7e2f321970a2e970ba30e272127892013b61c63be66431a318d32b52c3f
4
- data.tar.gz: 4d213ad75e6973aa6ef95bee7fb39a62bca69cd3261fc18e2bfcfc9a6b913b87
3
+ metadata.gz: c36ed17ae02ff9398179ca95187a072f69aa7de8f610986cf2a69592cbccaf69
4
+ data.tar.gz: f06bed189b4a25049541e2e4a0d25dd3cb26df3b76b989c6839c5b512bf37d41
5
5
  SHA512:
6
- metadata.gz: b7b00a033d247555ca57acc503c88de73111605738998a9e5b3ea4e53f0353b08df19cfa1f9d5c84a5ad22b5528ddb632ba927b4f0b53fb5493f5391732d3d13
7
- data.tar.gz: 23cec6ae8fa9b9ca04cf2242c86f8310de76a191b5a66bc4f2cab09aff7ea5c3e0a0d312bfe582dd2150d344302bdabab592c9b1944385bd75ae86de2367d207
6
+ metadata.gz: 960adee4eea590e6ae820a336775a051a2c170a55af32add89dfcf4e8bf44ae8f96ec678ff021be89d6e0a065d7f32b91c1526631078f7311a9352789100c1c8
7
+ data.tar.gz: 2781130d839a11d5bc1c30ae25a3573d56715fa21eb39eae69018f7251e8ae5d5e5e9fdbc7a3866b951800a289d055543cd91fe06d8071371a1c1679e448b4cf
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.2.0
1
+ ### 0.2.1
2
2
 
3
3
  ### 0.0.1 / 2020-11-15
4
4
 
data/Manifest.txt CHANGED
@@ -4,17 +4,18 @@ README.md
4
4
  Rakefile
5
5
  bin/fbgen
6
6
  bin/fbtxt
7
+ bin/fbup
7
8
  config/leagues_america.csv
8
9
  config/leagues_europe.csv
9
10
  config/leagues_world.csv
10
11
  config/openfootball.csv
11
- lib/sportdb/fbgen/github.rb
12
- lib/sportdb/fbgen/github_config.rb
13
12
  lib/sportdb/fbgen/main.rb
14
13
  lib/sportdb/fbtxt/main.rb
14
+ lib/sportdb/fbup/github.rb
15
+ lib/sportdb/fbup/github_config.rb
16
+ lib/sportdb/fbup/main.rb
15
17
  lib/sportdb/writers.rb
16
18
  lib/sportdb/writers/goals.rb
17
19
  lib/sportdb/writers/league_config.rb
18
20
  lib/sportdb/writers/txt_writer.rb
19
21
  lib/sportdb/writers/version.rb
20
- lib/sportdb/writers/write.rb
data/Rakefile CHANGED
@@ -18,6 +18,7 @@ Hoe.spec 'sportdb-writers' do
18
18
  self.history_file = 'CHANGELOG.md'
19
19
 
20
20
  self.extra_deps = [
21
+ ['football-timezones'],
21
22
  ['sportdb-quick'],
22
23
  ['gitti'],
23
24
  ]
data/bin/fbup ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## tip: to test run:
4
+ ## ruby -I ./lib bin/fbup
5
+
6
+
7
+ require 'sportdb/writers'
8
+
9
+
10
+ Fbup.main( ARGV )
11
+
12
+
13
+ puts "bye"
@@ -2,5 +2,12 @@ key, name, basename, start_season, end_season, co
2
2
 
3
3
  ar.1, Argentina Primera Division, 1-primeradivision,,,
4
4
  br.1, Brasileiro Série A, 1-seriea,,,
5
+ br.2, Brasileiro Série B, 1-serieb,,,
6
+
7
+ mx.1, Liga MX, 1-ligamx,,, ## note: basename gets overwritten by stages e.g. 1-apertura or 1-clausura
8
+ mx.2, Liga de Expansión MX, 2-ligaexpansionmx, 2020/21,,
9
+ mx.2, Ascenso MX, 2-ascensomx, ,2019/20,
10
+
11
+
12
+ copa.l, Copa Libertadores, libertadores,,,
5
13
 
6
- mx.1, Liga MX, 1-ligamx,,, ## note: basename gets overwritten by stages e.g. 1-apertura or 1-clausura
@@ -92,3 +92,13 @@ ru.1, Russian Premier League, 1-premierliga,,,
92
92
  ru.2, Russian 1. Division, 2-division1,,,
93
93
 
94
94
 
95
+
96
+ ## use uefa.1 - why? why not?
97
+ ## uefa.2,
98
+ ## uefa.3
99
+ uefa.cl, UEFA Champions League, cl,,, ### use champs or such?
100
+ uefa.el, UEFA Europa League, el,,, ### use europa
101
+ uefa.conf, UEFA Conference League, conf,,, ### use con (conf - confusion with config files?)
102
+
103
+ uefa.nl, UEFA Nations League, nl,,,
104
+ euro, Euro, euro,,,
@@ -2,3 +2,7 @@ key, name, basename, start_season, end_season, co
2
2
 
3
3
  cn.1, Chinese Super League, 1-superleague,,,
4
4
  jp.1, Japan J1 League, 1-j1league,,,
5
+
6
+
7
+
8
+ world, World Cup, cup,,,
@@ -42,11 +42,25 @@ ua, europe/ukraine
42
42
  ru, europe/russia
43
43
 
44
44
 
45
+ uefa.cl, europe-champions-league
46
+ uefa.el, europe-champions-league
47
+ uefa.conf, europe-champions-league
48
+
49
+ euro, euro
50
+
51
+
52
+
45
53
  mx, mexico
46
54
 
47
55
  ar, south-america/argentina
48
56
  br, south-america/brazil
49
57
 
58
+ copa.l, copa-libertadores
59
+ copa.s, copa-libertadores
60
+
61
+
50
62
  cn, world/asia/china
51
63
  jp, world/asia/japan
52
64
 
65
+
66
+ world, worldcup
@@ -4,8 +4,7 @@ def self.main( args=ARGV )
4
4
 
5
5
  opts = {
6
6
  source_path: [],
7
- push: false,
8
- dry: false, ## dry run (no write)
7
+ dry: false,
9
8
  debug: true,
10
9
  file: nil,
11
10
  }
@@ -13,10 +12,6 @@ opts = {
13
12
  parser = OptionParser.new do |parser|
14
13
  parser.banner = "Usage: #{$PROGRAM_NAME} [options] [args]"
15
14
 
16
- parser.on( "-p", "--push",
17
- "fast forward sync and commit & push changes to git repo - default is (#{opts[:push]})" ) do |push|
18
- opts[:push] = push
19
- end
20
15
  parser.on( "--dry",
21
16
  "dry run; do NOT write - default is (#{opts[:dry]})" ) do |dry|
22
17
  opts[:dry] = dry
@@ -26,8 +21,13 @@ parser = OptionParser.new do |parser|
26
21
  opts[:debug] = !debug
27
22
  end
28
23
 
24
+ parser.on( "-I DIR", "--include DIR",
25
+ "add directory to (source) search path - default is (#{opts[:source_path].join(',')})") do |dir|
26
+ opts[:source_path] += path
27
+ end
28
+
29
29
  parser.on( "-f FILE", "--file FILE",
30
- "read leagues via .csv file") do |file|
30
+ "read leagues (and seasons) via .csv file") do |file|
31
31
  opts[:file] = file
32
32
  end
33
33
  end
@@ -38,71 +38,54 @@ parser.parse!( args )
38
38
  if opts[:source_path].empty? &&
39
39
  File.exist?( '/sports/cache.api.fbdat') &&
40
40
  File.exist?( '/sports/cache.wfb' )
41
- opts[:source_path] << '/sports/cache.api.fbdat'
42
- opts[:source_path] << '/sports/cache.wfb'
41
+ opts[:source_path] << '/sports/cache.api.fbdat'
42
+ opts[:source_path] << '/sports/cache.wfb'
43
+ end
44
+
45
+
46
+ if opts[:source_path].empty?
47
+ opts[:source_path] = ['.'] ## use ./ as default
43
48
  end
44
49
 
50
+ source_path = opts[:source_path]
51
+
45
52
 
46
53
  puts "OPTS:"
47
54
  p opts
48
55
  puts "ARGV:"
49
56
  p args
50
57
 
58
+
51
59
  datasets = if opts[:file]
52
60
  read_datasets( opts[:file] )
53
61
  else
54
- parse_datasets( args )
62
+ parse_datasets_args( args )
55
63
  end
56
64
 
57
65
  puts "datasets:"
58
66
  pp datasets
59
67
 
60
68
 
61
- source_path = opts[:source_path]
62
- source_path = ['.'] if source_path.empty? ## use ./ as default
63
-
64
- root_dir = if opts[:push]
65
- GitHubSync.root # e.g. "/sports"
66
- else
67
- './o'
68
- end
69
69
 
70
+ root_dir = './o'
70
71
  puts " (output) root_dir: >#{root_dir}<"
71
72
 
72
- sync = if opts[:push]
73
- repos = GitHubSync.find_repos( datasets )
74
- puts " #{repos.size} repo(s):"
75
- pp repos
76
- GitHubSync.new( repos )
77
- else
78
- nil
79
- end
80
- puts " sync:"
81
- pp sync
82
-
83
- sync.git_fast_forward_if_clean if sync
84
-
85
73
 
74
+ ### step 0 - validate and fill-in seasons etc.
75
+ validate_datasets!( datasets, source_path: source_path )
86
76
 
77
+ ## step 1 - generate
87
78
  datasets.each do |league_key, seasons|
88
- seasons = [ Season('2024/25') ] if seasons.empty?
89
-
90
79
  puts "==> gen #{league_key} - #{seasons.size} seasons(s)..."
91
80
 
92
81
  league_info = Writer::LEAGUES[ league_key ]
93
82
  pp league_info
94
83
 
95
84
  seasons.each do |season|
96
- ### get matches
97
-
98
85
  filename = "#{season.to_path}/#{league_key}.csv"
99
86
  path = find_file( filename, path: source_path )
100
87
 
101
- if path.nil?
102
- puts "!! no source found for #{filename}; sorry"
103
- exit 1
104
- end
105
-
88
+ ## get matches
106
89
  puts " ---> reading matches in #{path} ..."
107
90
  matches = SportDb::CsvMatchParser.read( path )
108
91
  puts " #{matches.size} matches"
@@ -112,20 +95,14 @@ datasets.each do |league_key, seasons|
112
95
  puts txt if opts[:debug]
113
96
 
114
97
  league_name = league_info[ :name ] # e.g. Brasileiro Série A
115
- basename = league_info[ :basename] #.e.g 1-seriea
116
-
117
98
  league_name = league_name.call( season ) if league_name.is_a?( Proc ) ## is proc/func - name depends on season
118
- basename = basename.call( season ) if basename.is_a?( Proc ) ## is proc/func - name depends on season
119
99
 
120
100
  buf = String.new
121
101
  buf << "= #{league_name} #{season}\n\n"
122
102
  buf << txt
123
103
 
124
- repo = GitHubSync::REPOS[ league_key ]
125
- repo_path = "#{repo['owner']}/#{repo['name']}"
126
- repo_path << "/#{repo['path']}" if repo['path'] ## note: do NOT forget to add optional extra path!!!
104
+ outpath = "#{root_dir}/foot/#{season.to_path}/#{league_key}.txt"
127
105
 
128
- outpath = "#{root_dir}/#{repo_path}/#{season.to_path}/#{basename}.txt"
129
106
  if opts[:dry]
130
107
  puts " (dry) writing to >#{outpath}<..."
131
108
  else
@@ -133,79 +110,63 @@ datasets.each do |league_key, seasons|
133
110
  end
134
111
  end
135
112
  end
136
-
137
- sync.git_push_if_changes if sync
138
-
139
113
  end # method self.main
140
114
 
141
115
 
142
116
 
143
- def self.parse_datasets( args )
144
- ### split args in datasets with leagues and seasons
145
- datasets = []
146
- args.each do |arg|
147
- if arg =~ %r{^[0-9/-]+$} ## season
148
- if datasets.empty?
149
- puts "!! ERROR - league required before season arg; sorry"
150
- exit 1
151
- end
152
-
153
- season = Season.parse( arg ) ## check season
154
- datasets[-1][1] << season
155
- else ## assume league key
156
- key = arg.downcase
157
- league_info = Writer::LEAGUES[ key ]
158
-
159
- if league_info.nil?
160
- puts "!! ERROR - no league found for >#{key}<; sorry"
161
- exit 1
162
- end
117
+ def self.find_file( filename, path: )
118
+ path.each do |src_dir|
119
+ path = "#{src_dir}/#{filename}"
120
+ return path if File.exist?( path )
121
+ end
163
122
 
164
- datasets << [key, []]
165
- end
166
- end
167
- datasets
123
+ ## fix - raise file not found error!!!
124
+ nil ## not found - raise filenot found error - why? why not?
168
125
  end
169
126
 
170
127
 
171
- def self.read_datasets( path )
172
- ### split args in datasets with leagues and seasons
173
- datasets = []
174
- recs = read_csv( path )
175
- recs.each do |rec|
176
- league_code = rec['league']
177
- key = league_code.downcase
178
- league_info = Writer::LEAGUES[ key ]
128
+ ### use a function for (re)use
129
+ ### note - may add seasons in place!! (if seasons is empty)
130
+ def self.validate_datasets!( datasets, source_path: )
131
+ datasets.each do |dataset|
132
+ league_key, seasons = dataset
179
133
 
134
+ league_info = Writer::LEAGUES[ league_key ]
180
135
  if league_info.nil?
181
- puts "!! ERROR - no league found for >#{key}<; sorry"
136
+ puts "!! ERROR - no league (config) found for >#{league_key}<; sorry"
182
137
  exit 1
183
138
  end
184
139
 
185
- datasets << [key, []]
186
140
 
187
- seasons_str = rec['seasons']
188
- seasons = seasons_str.split( /[ ]+/ )
141
+ if seasons.empty?
142
+ ## simple heuristic to find current season
143
+ [ Season( '2024/25'), Season( '2024') ].each do |season|
144
+ filename = "#{season.to_path}/#{league_key}.csv"
145
+ path = find_file( filename, path: source_path )
146
+ if path
147
+ seasons = [season]
148
+ dataset[1] = seasons
149
+ break
150
+ end
151
+ end
189
152
 
190
- seasons.each do |season_str|
191
- season = Season.parse( season_str ) ## check season
192
- datasets[-1][1] << season
153
+ if seasons.empty?
154
+ puts "!! ERROR - no latest auto-season via source found for #{league_key}; sorry"
155
+ exit 1
156
+ end
193
157
  end
194
- end
195
- datasets
196
- end
197
158
 
159
+ ## check source path too upfronat - why? why not?
160
+ seasons.each do |season|
161
+ filename = "#{season.to_path}/#{league_key}.csv"
162
+ path = find_file( filename, path: source_path )
198
163
 
199
- def self.find_file( filename, path: )
200
- path.each do |src_dir|
201
- path = "#{src_dir}/#{filename}"
202
- return path if File.exist?( path )
164
+ if path.nil?
165
+ puts "!! ERROR - no source found for #{filename}; sorry"
166
+ exit 1
167
+ end
203
168
  end
204
-
205
- ## fix - raise file not found error!!!
206
- nil ## not found - raise filenot found error - why? why not?
169
+ end
170
+ datasets
207
171
  end
208
-
209
-
210
-
211
172
  end # module Fbgen
@@ -1,5 +1,5 @@
1
1
 
2
- module Fbgen
2
+ module Fbup
3
3
 
4
4
  ###
5
5
  ## todo/fix:
@@ -80,4 +80,4 @@ def _git_fast_forward_if_clean( pathspec )
80
80
  end
81
81
  end
82
82
  end # class GitHub
83
- end # module Fbgen
83
+ end # module Fbup
@@ -1,5 +1,5 @@
1
1
 
2
- module Fbgen
2
+ module Fbup
3
3
  class GitHubConfig
4
4
 
5
5
  ## map leagues to repo+path
@@ -91,7 +91,7 @@ def find_repo( key )
91
91
  end
92
92
 
93
93
  end # class GitHubConfig
94
- end # module Fbgen
94
+ end # module Fbup
95
95
 
96
96
 
97
97
 
@@ -0,0 +1,159 @@
1
+
2
+ module Fbup
3
+ def self.main( args=ARGV )
4
+
5
+ opts = {
6
+ source_path: [],
7
+ push: false,
8
+ ffwd: false,
9
+ dry: false, ## dry run (no write)
10
+ test: true, ## sets push & ffwd to false
11
+ debug: true,
12
+ file: nil,
13
+ test_dir: './o',
14
+ }
15
+
16
+
17
+ parser = OptionParser.new do |parser|
18
+ parser.banner = "Usage: #{$PROGRAM_NAME} [options] [args]"
19
+
20
+ parser.on( "-p", "--[no-]push",
21
+ "fast forward sync and commit & push changes to git repo - default is (#{opts[:push]})" ) do |push|
22
+ opts[:push] = push
23
+ opts[:fwd] = true if opts[:push] ## note: autoset ffwd too if push == true
24
+ end
25
+ ## todo/check - add a --ffwd flag too - why? why not?
26
+
27
+ parser.on( "-t", "--test",
28
+ "test run; writing output to #{opts[:test_dir]} - default is #{opts[:test]}" ) do |test|
29
+ opts[:test] = test
30
+ opts[:push] = false
31
+ opts[:ffwd] = false
32
+ end
33
+ parser.on( "--dry",
34
+ "dry run; do NOT write - default is (#{opts[:dry]})" ) do |dry|
35
+ opts[:dry] = dry
36
+ opts[:push] = false ### autoset push & ffwd - why? why not?
37
+ opts[:ffwd] = false
38
+ end
39
+
40
+ parser.on( "-q", "--quiet",
41
+ "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
42
+ opts[:debug] = !debug
43
+ end
44
+
45
+ parser.on( "-I DIR", "--include DIR",
46
+ "add directory to (source) search path - default is (#{opts[:source_path].join(',')})") do |dir|
47
+ opts[:source_path] += path
48
+ end
49
+
50
+ parser.on( "-f FILE", "--file FILE",
51
+ "read leagues (and seasons) via .csv file") do |file|
52
+ opts[:file] = file
53
+ end
54
+ end
55
+ parser.parse!( args )
56
+
57
+
58
+ if opts[:source_path].empty? &&
59
+ File.exist?( '/sports/cache.api.fbdat') &&
60
+ File.exist?( '/sports/cache.wfb' )
61
+ opts[:source_path] << '/sports/cache.api.fbdat'
62
+ opts[:source_path] << '/sports/cache.wfb'
63
+ end
64
+
65
+ if opts[:source_path].empty?
66
+ opts[:source_path] = ['.'] ## use ./ as default
67
+ end
68
+
69
+
70
+
71
+ puts "OPTS:"
72
+ p opts
73
+ puts "ARGV:"
74
+ p args
75
+
76
+
77
+ datasets = if opts[:file]
78
+ read_datasets( opts[:file] )
79
+ else
80
+ parse_datasets_args( args )
81
+ end
82
+
83
+ puts "datasets:"
84
+ pp datasets
85
+
86
+
87
+ source_path = opts[:source_path]
88
+
89
+ root_dir = if opts[:test]
90
+ opts[:test_dir]
91
+ else
92
+ GitHubSync.root # e.g. "/sports"
93
+ end
94
+
95
+ puts " (output) root_dir: >#{root_dir}<"
96
+
97
+ repos = GitHubSync.find_repos( datasets )
98
+ puts " #{repos.size} repo(s):"
99
+ pp repos
100
+ sync = GitHubSync.new( repos )
101
+
102
+ puts " sync:"
103
+ pp sync
104
+
105
+
106
+ sync.git_fast_forward_if_clean if opts[:ffwd]
107
+
108
+ ### step 0 - validate and fill-in seasons etc.
109
+ ## reuse from Fbgen tool
110
+ Fbgen.validate_datasets!( datasets, source_path: source_path )
111
+
112
+
113
+ datasets.each do |league_key, seasons|
114
+ puts "==> gen #{league_key} - #{seasons.size} seasons(s)..."
115
+
116
+ league_info = Writer::LEAGUES[ league_key ]
117
+ pp league_info
118
+
119
+ seasons.each do |season|
120
+ filename = "#{season.to_path}/#{league_key}.csv"
121
+ path = Fbgen.find_file( filename, path: source_path )
122
+
123
+ ### get matches
124
+ puts " ---> reading matches in #{path} ..."
125
+ matches = SportDb::CsvMatchParser.read( path )
126
+ puts " #{matches.size} matches"
127
+
128
+ ## build
129
+ txt = SportDb::TxtMatchWriter.build( matches )
130
+ puts txt if opts[:debug]
131
+
132
+ league_name = league_info[ :name ] # e.g. Brasileiro Série A
133
+ basename = league_info[ :basename] #.e.g 1-seriea
134
+
135
+ league_name = league_name.call( season ) if league_name.is_a?( Proc ) ## is proc/func - name depends on season
136
+ basename = basename.call( season ) if basename.is_a?( Proc ) ## is proc/func - name depends on season
137
+
138
+ buf = String.new
139
+ buf << "= #{league_name} #{season}\n\n"
140
+ buf << txt
141
+
142
+ repo = GitHubSync::REPOS[ league_key ]
143
+ repo_path = "#{repo['owner']}/#{repo['name']}"
144
+ repo_path << "/#{repo['path']}" if repo['path'] ## note: do NOT forget to add optional extra path!!!
145
+
146
+ outpath = "#{root_dir}/#{repo_path}/#{season.to_path}/#{basename}.txt"
147
+
148
+ if opts[:dry]
149
+ puts " (dry) writing to >#{outpath}<..."
150
+ else
151
+ write_text( outpath, buf )
152
+ end
153
+ end
154
+ end
155
+
156
+ sync.git_push_if_changes if opts[:push]
157
+
158
+ end # method self.main
159
+ end # module Fbgen
@@ -4,7 +4,7 @@ module Module
4
4
  module Writers
5
5
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
6
6
  MINOR = 2
7
- PATCH = 0
7
+ PATCH = 1
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
@@ -26,11 +26,6 @@ end # module Writer
26
26
  ###
27
27
  # our own code
28
28
  require_relative 'writers/version'
29
- require_relative 'writers/txt_writer'
30
-
31
- require_relative 'writers/goals'
32
- require_relative 'writers/write'
33
-
34
29
 
35
30
  ## setup leagues (info) table
36
31
  require_relative 'writers/league_config'
@@ -48,9 +43,16 @@ module Writer
48
43
  end # module Writer
49
44
 
50
45
 
46
+ require_relative 'writers/goals'
47
+ require_relative 'writers/txt_writer'
48
+
49
+
51
50
  ###
52
51
  # fbtxt tool
52
+ require 'football/timezones' ## pulls in read_datasets, etc.
53
+
53
54
  require_relative 'fbtxt/main'
55
+ require_relative 'fbgen/main'
54
56
 
55
57
 
56
58
 
@@ -58,11 +60,11 @@ require_relative 'fbtxt/main'
58
60
  # push & pull github scripts
59
61
  require 'gitti' ## note - requires git machinery
60
62
 
61
- require_relative 'fbgen/github_config'
62
- require_relative 'fbgen/github' ## github helpers/update machinery
63
+ require_relative 'fbup/github_config'
64
+ require_relative 'fbup/github' ## github helpers/update machinery
63
65
 
64
66
 
65
- module Fbgen
67
+ module Fbup
66
68
  class GitHubSync
67
69
  REPOS = GitHubConfig.new
68
70
  recs = read_csv( "#{SportDb::Module::Writers.root}/config/openfootball.csv" )
@@ -96,10 +98,10 @@ def self.find_repos( datasets )
96
98
  repos.uniq ## note: remove duplicates (e.g. europe or world or such)
97
99
  end
98
100
  end # class GitHubSync
99
- end # module Fbgen
101
+ end # module Fbup
100
102
 
101
103
 
102
- require_relative 'fbgen/main'
104
+ require_relative 'fbup/main'
103
105
 
104
106
 
105
107
 
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sportdb-writers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
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-13 00:00:00.000000000 Z
11
+ date: 2024-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: football-timezones
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: sportdb-quick
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -78,6 +92,7 @@ email: gerald.bauer@gmail.com
78
92
  executables:
79
93
  - fbgen
80
94
  - fbtxt
95
+ - fbup
81
96
  extensions: []
82
97
  extra_rdoc_files:
83
98
  - CHANGELOG.md
@@ -90,20 +105,21 @@ files:
90
105
  - Rakefile
91
106
  - bin/fbgen
92
107
  - bin/fbtxt
108
+ - bin/fbup
93
109
  - config/leagues_america.csv
94
110
  - config/leagues_europe.csv
95
111
  - config/leagues_world.csv
96
112
  - config/openfootball.csv
97
- - lib/sportdb/fbgen/github.rb
98
- - lib/sportdb/fbgen/github_config.rb
99
113
  - lib/sportdb/fbgen/main.rb
100
114
  - lib/sportdb/fbtxt/main.rb
115
+ - lib/sportdb/fbup/github.rb
116
+ - lib/sportdb/fbup/github_config.rb
117
+ - lib/sportdb/fbup/main.rb
101
118
  - lib/sportdb/writers.rb
102
119
  - lib/sportdb/writers/goals.rb
103
120
  - lib/sportdb/writers/league_config.rb
104
121
  - lib/sportdb/writers/txt_writer.rb
105
122
  - lib/sportdb/writers/version.rb
106
- - lib/sportdb/writers/write.rb
107
123
  homepage: https://github.com/sportdb/sport.db
108
124
  licenses:
109
125
  - Public Domain
@@ -1,272 +0,0 @@
1
-
2
- module Writer
3
-
4
-
5
- class Job ## todo/check: use a module (and NOT a class) - why? why not?
6
- def self.write( datasets, source:,
7
- normalize: false )
8
- datasets.each_with_index do |dataset,i|
9
- league = dataset[0]
10
- seasons = dataset[1]
11
-
12
- puts "writing [#{i+1}/#{datasets.size}] #{league}..."
13
- seasons.each_with_index do |season,j|
14
- puts " season [#{j+1}/#{season.size}] #{league} #{season}..."
15
- Writer.write( league: league,
16
- season: season,
17
- source: source,
18
- normalize: normalize )
19
- end
20
- end
21
- end
22
- end # class Job
23
-
24
-
25
-
26
-
27
- def self.split_matches( matches, season: )
28
- matches_i = []
29
- matches_ii = []
30
- matches.each do |match|
31
- date = Date.strptime( match.date, '%Y-%m-%d' )
32
- if date.year == season.start_year
33
- matches_i << match
34
- elsif date.year == season.end_year
35
- matches_ii << match
36
- else
37
- puts "!! ERROR: match date-out-of-range for season:"
38
- pp season
39
- pp date
40
- pp match
41
- exit 1
42
- end
43
- end
44
- [matches_i, matches_ii]
45
- end
46
-
47
-
48
-
49
- ##
50
- ## note: default - do NOT normalize any more
51
-
52
- def self.write( league:, season:,
53
- source:,
54
- extra: nil,
55
- split: false,
56
- normalize: false,
57
- rounds: true )
58
- season = Season( season ) ## normalize season
59
-
60
- league_info = LEAGUES[ league ]
61
- if league_info.nil?
62
- puts "!! ERROR - no league found for >#{league}<; sorry"
63
- exit 1
64
- end
65
-
66
- ## check - if source is directory (assume if starting ./ or ../ or /)
67
- ## check if directory exists
68
- ## todo/fix - use Dir.exist? why? why not?
69
- unless File.exist?( source )
70
- puts "!! ERROR: source dir >#{source}< does not exist"
71
- exit 1
72
- end
73
- source_info = { path: source } ## wrap in "plain" source dir in source info
74
-
75
- source_path = source_info[:path]
76
-
77
- ## format lets you specify directory layout
78
- ## default = 1888-89
79
- ## century = 1800s/1888-89
80
- ## ...
81
- season_path = season.to_path( (source_info[:format] || 'default').to_sym )
82
- in_path = "#{source_path}/#{season_path}/#{league}.csv" # e.g. ../stage/one/2020/br.1.csv
83
-
84
-
85
- matches = SportDb::CsvMatchParser.read( in_path )
86
- puts "matches- #{matches.size} records"
87
-
88
-
89
- ## check for goals
90
- in_path_goals = "#{source_path}/#{season_path}/#{league}~goals.csv" # e.g. ../stage/one/2020/br.1~goals.csv
91
- if File.exist?( in_path_goals )
92
- goals = SportDb::CsvGoalParser.read( in_path_goals )
93
- puts "goals - #{goals.size} records"
94
- pp goals[0]
95
-
96
- puts
97
- puts "merge goals:"
98
- merge_goals( matches, goals )
99
- end
100
-
101
-
102
- pp matches[0]
103
-
104
-
105
- if normalize
106
- if normalize.is_a?(Proc)
107
- matches = normalize.call( matches, league: league,
108
- season: season )
109
- else
110
- puts "!! ERROR - normalize; expected proc got #{normalize.inspect}"
111
- exit 1
112
- end
113
- end
114
-
115
-
116
-
117
- league_name = league_info[ :name ] # e.g. Brasileiro Série A
118
- basename = league_info[ :basename] #.e.g 1-seriea
119
-
120
- league_name = league_name.call( season ) if league_name.is_a?( Proc ) ## is proc/func - name depends on season
121
- basename = basename.call( season ) if basename.is_a?( Proc ) ## is proc/func - name depends on season
122
-
123
- ## note - repo_path moved!!!
124
- ## repo_path = league_info[ :path ] # e.g. brazil or world/europe/portugal etc.
125
- repo = SportDb::GitHubSync::REPOS[ league ]
126
- repo_path = "#{repo['owner']}/#{repo['name']}"
127
- repo_path << "/#{repo['path']}" if repo['path'] ## note: do NOT forget to add optional extra path!!!
128
-
129
-
130
-
131
- season_path = String.new ## note: allow extra path for output!!!! e.g. archive/2000s etc.
132
- season_path << "#{extra}/" if extra
133
- season_path << season.path
134
-
135
-
136
- ## check for stages
137
- stages = league_info[ :stages ]
138
- stages = stages.call( season ) if stages.is_a?( Proc ) ## is proc/func - stages depends on season
139
-
140
-
141
- if stages
142
-
143
- ## split into four stages / two files
144
- ## - Grunddurchgang
145
- ## - Finaldurchgang - Meister
146
- ## - Finaldurchgang - Qualifikation
147
- ## - Europa League Play-off
148
-
149
- matches_by_stage = matches.group_by { |match| match.stage }
150
- pp matches_by_stage.keys
151
-
152
-
153
- ## stages = prepare_stages( stages )
154
- pp stages
155
-
156
-
157
- romans = %w[I II III IIII V VI VII VIII VIIII X XI] ## note: use "simple" romans without -1 rule e.g. iv or ix
158
-
159
- stages.each_with_index do |stage, i|
160
-
161
- ## assume "extended" style / syntax
162
- if stage.is_a?( Hash ) && stage.has_key?( :names )
163
- stage_names = stage[ :names ]
164
- stage_basename = stage[ :basename ]
165
- ## add search/replace {basename} - why? why not?
166
- stage_basename = stage_basename.sub( '{basename}', basename )
167
- else ## assume simple style (array of strings OR hash mapping of string => string)
168
- stage_names = stage
169
- stage_basename = if stages.size == 1
170
- "#{basename}" ## use basename as is 1:1
171
- else
172
- "#{basename}-#{romans[i].downcase}" ## append i,ii,etc.
173
- end
174
- end
175
-
176
- buf = build_stage( matches_by_stage, stages: stage_names,
177
- name: "#{league_name} #{season.key}"
178
- )
179
-
180
- ## note: might be empty!!! if no matches skip (do NOT write)
181
- write_text( "#{config.out_dir}/#{repo_path}/#{season_path}/#{stage_basename}.txt",
182
- buf ) unless buf.empty?
183
- end
184
- else ## no stages - assume "regular" plain vanilla season
185
-
186
- ## always (auto-) sort for now - why? why not?
187
- matches = matches.sort do |l,r|
188
- ## first by date (older first)
189
- ## next by matchday (lower first)
190
- res = l.date <=> r.date
191
- res = l.time <=> r.time if res == 0 && l.time && r.time
192
- res = l.round <=> r.round if res == 0 && rounds
193
- res
194
- end
195
-
196
- if split
197
- matches_i, matches_ii = split_matches( matches, season: season )
198
-
199
- out_path = "#{config.out_dir}/#{repo_path}/#{season_path}/#{basename}-i.txt"
200
-
201
- SportDb::TxtMatchWriter.write( out_path, matches_i,
202
- name: "#{league_name} #{season.key}",
203
- rounds: rounds )
204
-
205
- out_path = "#{config.out_dir}/#{repo_path}/#{season_path}/#{basename}-ii.txt"
206
-
207
- SportDb::TxtMatchWriter.write( out_path, matches_ii,
208
- name: "#{league_name} #{season.key}",
209
- rounds: rounds )
210
- else
211
- out_path = "#{config.out_dir}/#{repo_path}/#{season_path}/#{basename}.txt"
212
-
213
- SportDb::TxtMatchWriter.write( out_path, matches,
214
- name: "#{league_name} #{season.key}",
215
- rounds: rounds )
216
- end
217
- end
218
- end
219
-
220
-
221
- =begin
222
- def prepare_stages( stages )
223
- if stages.is_a?( Array )
224
- if stages[0].is_a?( Array ) ## is array of array
225
- ## convert inner array shortcuts to hash - stage input is same as stage output
226
- stages.map {|ary| ary.reduce({}) {|h,stage| h[stage]=stage; h }}
227
- elsif stages[0].is_a?( Hash ) ## assume array of hashes
228
- stages ## pass through as is ("canonical") format!!!
229
- else ## assume array of strings
230
- ## assume single array shortcut; convert to hash - stage input is same as stage output name
231
- stages = stages.reduce({}) {|h,stage| h[stage]=stage; h }
232
- [stages] ## return hash wrapped in array
233
- end
234
- else ## assume (single) hash
235
- [stages] ## always return array of hashes
236
- end
237
- end
238
- =end
239
-
240
-
241
-
242
- def self.build_stage( matches_by_stage, stages:, name: )
243
- buf = String.new
244
-
245
- ## note: allow convenience shortcut - assume stage_in is stage_out - auto-convert
246
- stages = stages.reduce({}) {|h,stage| h[stage]=stage; h } if stages.is_a?( Array )
247
-
248
- stages.each_with_index do |(stage_in, stage_out),i|
249
- matches = matches_by_stage[ stage_in ] ## todo/fix: report error if no matches found!!!
250
-
251
- next if matches.nil? || matches.empty?
252
-
253
- ## (auto-)sort matches by
254
- ## 1) date
255
- matches = matches.sort do |l,r|
256
- result = l.date <=> r.date
257
- result
258
- end
259
-
260
- buf << "\n\n" if i > 0 && buf.size > 0
261
-
262
- buf << "= #{name}, #{stage_out}\n"
263
- buf << SportDb::TxtMatchWriter.build( matches )
264
-
265
- puts buf
266
- end
267
-
268
- buf
269
- end
270
-
271
-
272
- end # module Writer