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 +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +4 -3
- data/Rakefile +1 -0
- data/bin/fbup +13 -0
- data/config/leagues_america.csv +8 -1
- data/config/leagues_europe.csv +10 -0
- data/config/leagues_world.csv +4 -0
- data/config/openfootball.csv +14 -0
- data/lib/sportdb/fbgen/main.rb +63 -102
- data/lib/sportdb/{fbgen → fbup}/github.rb +2 -2
- data/lib/sportdb/{fbgen → fbup}/github_config.rb +2 -2
- data/lib/sportdb/fbup/main.rb +159 -0
- data/lib/sportdb/writers/version.rb +1 -1
- data/lib/sportdb/writers.rb +12 -10
- metadata +21 -5
- data/lib/sportdb/writers/write.rb +0 -272
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c36ed17ae02ff9398179ca95187a072f69aa7de8f610986cf2a69592cbccaf69
|
4
|
+
data.tar.gz: f06bed189b4a25049541e2e4a0d25dd3cb26df3b76b989c6839c5b512bf37d41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 960adee4eea590e6ae820a336775a051a2c170a55af32add89dfcf4e8bf44ae8f96ec678ff021be89d6e0a065d7f32b91c1526631078f7311a9352789100c1c8
|
7
|
+
data.tar.gz: 2781130d839a11d5bc1c30ae25a3573d56715fa21eb39eae69018f7251e8ae5d5e5e9fdbc7a3866b951800a289d055543cd91fe06d8071371a1c1679e448b4cf
|
data/CHANGELOG.md
CHANGED
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
data/bin/fbup
ADDED
data/config/leagues_america.csv
CHANGED
@@ -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
|
data/config/leagues_europe.csv
CHANGED
@@ -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,,,
|
data/config/leagues_world.csv
CHANGED
data/config/openfootball.csv
CHANGED
@@ -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
|
data/lib/sportdb/fbgen/main.rb
CHANGED
@@ -4,8 +4,7 @@ def self.main( args=ARGV )
|
|
4
4
|
|
5
5
|
opts = {
|
6
6
|
source_path: [],
|
7
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
165
|
-
|
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
|
-
|
172
|
-
###
|
173
|
-
datasets
|
174
|
-
|
175
|
-
|
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 >#{
|
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
|
-
|
188
|
-
|
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
|
-
|
191
|
-
season
|
192
|
-
|
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
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
164
|
+
if path.nil?
|
165
|
+
puts "!! ERROR - no source found for #{filename}; sorry"
|
166
|
+
exit 1
|
167
|
+
end
|
203
168
|
end
|
204
|
-
|
205
|
-
|
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
|
@@ -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
|
data/lib/sportdb/writers.rb
CHANGED
@@ -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 '
|
62
|
-
require_relative '
|
63
|
+
require_relative 'fbup/github_config'
|
64
|
+
require_relative 'fbup/github' ## github helpers/update machinery
|
63
65
|
|
64
66
|
|
65
|
-
module
|
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
|
101
|
+
end # module Fbup
|
100
102
|
|
101
103
|
|
102
|
-
require_relative '
|
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.
|
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-
|
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
|