sportdb-writers 0.2.0 → 0.2.1

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: 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