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