fbtxt-pp 0.0.2
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 +7 -0
- data/CHANGELOG.md +4 -0
- data/Manifest.txt +34 -0
- data/README.md +25 -0
- data/Rakefile +32 -0
- data/bin/fbhub +18 -0
- data/bin/fbpp +14 -0
- data/lib/fbtxt-pp/config.rb +62 -0
- data/lib/fbtxt-pp/config_clubs.csv +37 -0
- data/lib/fbtxt-pp/config_more.rb +65 -0
- data/lib/fbtxt-pp/config_worldcup.rb +27 -0
- data/lib/fbtxt-pp/helper.rb +69 -0
- data/lib/fbtxt-pp/models/document.rb +130 -0
- data/lib/fbtxt-pp/models/goals.rb +60 -0
- data/lib/fbtxt-pp/models/match.rb +76 -0
- data/lib/fbtxt-pp/models/officials.rb +26 -0
- data/lib/fbtxt-pp/models/penalties.rb +29 -0
- data/lib/fbtxt-pp/models/players.rb +274 -0
- data/lib/fbtxt-pp/models/score.rb +85 -0
- data/lib/fbtxt-pp/models/stadiums.rb +114 -0
- data/lib/fbtxt-pp/models/teams.rb +161 -0
- data/lib/fbtxt-pp/pp/ppbookings.rb +55 -0
- data/lib/fbtxt-pp/pp/ppgoals.rb +71 -0
- data/lib/fbtxt-pp/pp/pplineup.rb +98 -0
- data/lib/fbtxt-pp/pp/ppmatch.rb +127 -0
- data/lib/fbtxt-pp/pp/ppmatch_full.rb +222 -0
- data/lib/fbtxt-pp/pp/ppmatch_min.rb +68 -0
- data/lib/fbtxt-pp/pp/ppopts.rb +92 -0
- data/lib/fbtxt-pp/pp/pppenalties.rb +32 -0
- data/lib/fbtxt-pp/pp/ppsquads.rb +224 -0
- data/lib/fbtxt-pp/pp/ppstats.rb +60 -0
- data/lib/fbtxt-pp/tool-fbhub.rb +204 -0
- data/lib/fbtxt-pp/tool-fbpp.rb +136 -0
- data/lib/fbtxt-pp/version.rb +24 -0
- data/lib/fbtxt-pp.rb +69 -0
- metadata +147 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: cb82751c433e1b2a9495f545dde77a538f1cb08b66e55aa6c6af0e3830ef6cff
|
|
4
|
+
data.tar.gz: 5cc0c7f57f4d5e79db8bba38d97dc3dbc4ca6374de7db8d51acf3fc7d351e681
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ec8a5d094a5054b95a7ee2fd7b2d798a181c4ad92e81f77a1d94bf571041032431d69fa85afbf90f07bd1bd3e35306a8d45a8a994a0989c6760e8321231d99d2
|
|
7
|
+
data.tar.gz: 32dea04aa3ad82a0033c8505794e2dd6fb959f0fdef51a00dbacd53a083358f0a2ac81628f1feccb745169e163f6c1301400de2c26487167aa2b0064f734551d
|
data/CHANGELOG.md
ADDED
data/Manifest.txt
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
CHANGELOG.md
|
|
2
|
+
Manifest.txt
|
|
3
|
+
README.md
|
|
4
|
+
Rakefile
|
|
5
|
+
bin/fbhub
|
|
6
|
+
bin/fbpp
|
|
7
|
+
lib/fbtxt-pp.rb
|
|
8
|
+
lib/fbtxt-pp/config.rb
|
|
9
|
+
lib/fbtxt-pp/config_clubs.csv
|
|
10
|
+
lib/fbtxt-pp/config_more.rb
|
|
11
|
+
lib/fbtxt-pp/config_worldcup.rb
|
|
12
|
+
lib/fbtxt-pp/helper.rb
|
|
13
|
+
lib/fbtxt-pp/models/document.rb
|
|
14
|
+
lib/fbtxt-pp/models/goals.rb
|
|
15
|
+
lib/fbtxt-pp/models/match.rb
|
|
16
|
+
lib/fbtxt-pp/models/officials.rb
|
|
17
|
+
lib/fbtxt-pp/models/penalties.rb
|
|
18
|
+
lib/fbtxt-pp/models/players.rb
|
|
19
|
+
lib/fbtxt-pp/models/score.rb
|
|
20
|
+
lib/fbtxt-pp/models/stadiums.rb
|
|
21
|
+
lib/fbtxt-pp/models/teams.rb
|
|
22
|
+
lib/fbtxt-pp/pp/ppbookings.rb
|
|
23
|
+
lib/fbtxt-pp/pp/ppgoals.rb
|
|
24
|
+
lib/fbtxt-pp/pp/pplineup.rb
|
|
25
|
+
lib/fbtxt-pp/pp/ppmatch.rb
|
|
26
|
+
lib/fbtxt-pp/pp/ppmatch_full.rb
|
|
27
|
+
lib/fbtxt-pp/pp/ppmatch_min.rb
|
|
28
|
+
lib/fbtxt-pp/pp/ppopts.rb
|
|
29
|
+
lib/fbtxt-pp/pp/pppenalties.rb
|
|
30
|
+
lib/fbtxt-pp/pp/ppsquads.rb
|
|
31
|
+
lib/fbtxt-pp/pp/ppstats.rb
|
|
32
|
+
lib/fbtxt-pp/tool-fbhub.rb
|
|
33
|
+
lib/fbtxt-pp/tool-fbpp.rb
|
|
34
|
+
lib/fbtxt-pp/version.rb
|
data/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# fbtxt-pp - pretty print (pp) football match fixtures & results in the football.txt format
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
* home :: [github.com/sportdb/sport.db](https://github.com/sportdb/sport.db)
|
|
5
|
+
* bugs :: [github.com/sportdb/sport.db/issues](https://github.com/sportdb/sport.db/issues)
|
|
6
|
+
* gem :: [rubygems.org/gems/fbtxt-pp](https://rubygems.org/gems/fbtxt-pp)
|
|
7
|
+
* rdoc :: [rubydoc.info/gems/fbtxt-pp](http://rubydoc.info/gems/fbtxt-pp)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
To be done
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## License
|
|
17
|
+
|
|
18
|
+
The `fbtxt-pp` scripts are dedicated to the public domain.
|
|
19
|
+
Use as you please with no restrictions whatsoever.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Questions? Comments?
|
|
23
|
+
|
|
24
|
+
Yes, you can. More than welcome.
|
|
25
|
+
See [Help & Support »](https://github.com/openfootball/help)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'hoe'
|
|
2
|
+
require './lib/fbtxt-pp/version.rb'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Hoe.spec 'fbtxt-pp' do
|
|
6
|
+
|
|
7
|
+
self.version = Fbtxt::Module::Fbpp::VERSION
|
|
8
|
+
|
|
9
|
+
self.summary = "fbtxt-pp - pretty print (pp) football match fixtures & results in the football.txt format"
|
|
10
|
+
self.description = summary
|
|
11
|
+
|
|
12
|
+
self.urls = { home: 'https://github.com/sportdb/sport.db' }
|
|
13
|
+
|
|
14
|
+
self.author = 'Gerald Bauer'
|
|
15
|
+
self.email = 'gerald.bauer@gmail.com'
|
|
16
|
+
|
|
17
|
+
# switch extension to .markdown for gihub formatting
|
|
18
|
+
self.readme_file = 'README.md'
|
|
19
|
+
self.history_file = 'CHANGELOG.md'
|
|
20
|
+
|
|
21
|
+
self.extra_deps = [
|
|
22
|
+
['cocos'],
|
|
23
|
+
['season-formats'],
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
self.licenses = ['Public Domain']
|
|
27
|
+
|
|
28
|
+
self.spec_extras = {
|
|
29
|
+
required_ruby_version: '>= 3.1.0'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
end
|
data/bin/fbhub
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
## tip: to test run:
|
|
4
|
+
## ruby -I ./lib bin/fbpp
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
## note - always use latest (local) if present
|
|
9
|
+
$LOAD_PATH.unshift( '/sports/yorobot/fbup/fbup/lib' )
|
|
10
|
+
require 'fbup'
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
## note - always use latest (local) if present
|
|
14
|
+
$LOAD_PATH.unshift( '/sports/yorobot/sport.db.more/fbtxt-pp/lib' )
|
|
15
|
+
require 'fbtxt-pp'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
Fbhub.main( ARGV )
|
data/bin/fbpp
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## defaults
|
|
5
|
+
FORMAT_OPTS_DEFAULTS = {
|
|
6
|
+
country: false,
|
|
7
|
+
city: false,
|
|
8
|
+
stadium: false,
|
|
9
|
+
timezone: false,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
FORMAT_OPTS_FULL_DEFAULTS = {
|
|
13
|
+
country: false,
|
|
14
|
+
city: true,
|
|
15
|
+
stadium: true,
|
|
16
|
+
timezone: true,
|
|
17
|
+
|
|
18
|
+
show_teams: false,
|
|
19
|
+
show_stadiums: false,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def _parse_format_opts( str )
|
|
23
|
+
h = {}
|
|
24
|
+
keys = str.split( /[ ]*\|[ ]*/ )
|
|
25
|
+
keys.each do |key|
|
|
26
|
+
case key.to_sym
|
|
27
|
+
when :country then h[:country] = true
|
|
28
|
+
when :city then h[:city] = true
|
|
29
|
+
else
|
|
30
|
+
raise ArgumentError, "unknown key #{key} in format opts"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
h
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def read_config_pp( *paths )
|
|
38
|
+
config = {}
|
|
39
|
+
paths.each do |path|
|
|
40
|
+
recs = read_csv( path )
|
|
41
|
+
|
|
42
|
+
recs.each do |rec|
|
|
43
|
+
key = rec['code']
|
|
44
|
+
h = {
|
|
45
|
+
slug: key,
|
|
46
|
+
name: rec['name'],
|
|
47
|
+
seasons: rec['seasons'],
|
|
48
|
+
opts: {}.merge( FORMAT_OPTS_DEFAULTS, _parse_format_opts( rec['opts'] )),
|
|
49
|
+
opts_full: {}.merge( FORMAT_OPTS_FULL_DEFAULTS, _parse_format_opts( rec['opts_full'] )),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
config[ key ] = h
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
config
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
CONFIGS = {}
|
|
60
|
+
more_configs = read_config_pp( "#{Fbtxt::Module::Fbpp.root}/lib/fbtxt-pp/config_clubs.csv" )
|
|
61
|
+
pp more_configs
|
|
62
|
+
CONFIGS.merge!( more_configs )
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
code, name, seasons, opts, opts_full
|
|
3
|
+
|
|
4
|
+
##
|
|
5
|
+
## remove seasons - why? why not?
|
|
6
|
+
|
|
7
|
+
at, Austria | Bundesliga, 2025/26,,
|
|
8
|
+
de, Germany | Bundesliga, 2025/26,,
|
|
9
|
+
eng, England | Premier League, 2025/26,,
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
fr, France | Ligue 1, 2025/26,,
|
|
13
|
+
it, Italy | Serie A, 2025/26,,
|
|
14
|
+
es, Spain | Primera División, 2025/26,,
|
|
15
|
+
|
|
16
|
+
mx, Mexico | Liga MX, 2025/26,,
|
|
17
|
+
|
|
18
|
+
uefa.cl, UEFA | Champions League, 2025/26, country|city, country
|
|
19
|
+
uefa.europa, UEFA | Europa League, 2025/26, country|city, country
|
|
20
|
+
uefa.con, UEFA | Conference League, 2025/26, country|city, country
|
|
21
|
+
|
|
22
|
+
###
|
|
23
|
+
# opts
|
|
24
|
+
# default as false everyting!! e.g.
|
|
25
|
+
# country: false,
|
|
26
|
+
# stadium: false,
|
|
27
|
+
# city: false,
|
|
28
|
+
# timezone: false
|
|
29
|
+
# { country: false,
|
|
30
|
+
# stadium: false,
|
|
31
|
+
# city: false,
|
|
32
|
+
# timezone: false,
|
|
33
|
+
# }
|
|
34
|
+
# opts_full
|
|
35
|
+
# default only country false
|
|
36
|
+
# { country: false,
|
|
37
|
+
# }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
|
|
2
|
+
### rename to setups or such - why? why not?
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## default setups
|
|
6
|
+
|
|
7
|
+
CONFIGS.merge!({
|
|
8
|
+
clubworldcup: {
|
|
9
|
+
slug: 'clubworldcup', ## rename to dir / indir / source / source or ???
|
|
10
|
+
name: 'Club World Cup',
|
|
11
|
+
seasons: ['2025'],
|
|
12
|
+
## default pp opts
|
|
13
|
+
opts: { country: true,
|
|
14
|
+
show_stadiums: false,
|
|
15
|
+
},
|
|
16
|
+
opts_full: { country: true,
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
## outdir = "../../openfootball/clubworldcup"
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## todo - find a better key than _v0??
|
|
25
|
+
## use clubworldcup_hist or _history or __ ??
|
|
26
|
+
clubworldcup_v0: {
|
|
27
|
+
## all club world cups 2000, 2005-2023
|
|
28
|
+
## NOT incl. new format every 4 yrs starting in 2025
|
|
29
|
+
## NOT incl. old/new interconti cup every yr starting in 2024
|
|
30
|
+
slug: 'interconticup',
|
|
31
|
+
name: 'Club World Cup',
|
|
32
|
+
## outname: 'clubworldcup',
|
|
33
|
+
seasons: ['2000',
|
|
34
|
+
'2005', '2006', '2007', '2008', '2009',
|
|
35
|
+
'2010', '2011', '2012', '2013', '2014',
|
|
36
|
+
'2015', '2016', '2017', '2018', '2019',
|
|
37
|
+
'2020', '2021', '2022', '2023'],
|
|
38
|
+
## default pp opts
|
|
39
|
+
opts: { country: true,
|
|
40
|
+
show_stadiums: false,
|
|
41
|
+
show_teams: true,
|
|
42
|
+
},
|
|
43
|
+
opts_full: { country: true,
|
|
44
|
+
show_teams: true,
|
|
45
|
+
},
|
|
46
|
+
## outdir = "../../openfootball/clubworldcup"
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
interconticup: {
|
|
52
|
+
## (new) interconti cup 2024-
|
|
53
|
+
slug: 'interconticup',
|
|
54
|
+
name: 'Intercontinental Cup',
|
|
55
|
+
seasons: ['2024', '2025'],
|
|
56
|
+
## default pp opts
|
|
57
|
+
opts: { country: true,
|
|
58
|
+
show_stadiums: false,
|
|
59
|
+
show_teams: true,
|
|
60
|
+
},
|
|
61
|
+
opts_full: { country: true,
|
|
62
|
+
show_teams: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
CONFIGS.merge!({
|
|
3
|
+
worldcup: {
|
|
4
|
+
slug: 'worldcup', ## rename to dir / indir / source / source or ???
|
|
5
|
+
name: 'World Cup',
|
|
6
|
+
## check - incl. 2026 - not all matches with results and teams!!!
|
|
7
|
+
seasons: ['1930', '1934', '1938',
|
|
8
|
+
'1950', '1954', '1958', '1962', '1966', '1970', '1974', '1978',
|
|
9
|
+
'1982', '1986', '1990', '1994', '1998', '2002', '2006', '2010',
|
|
10
|
+
'2014', '2018', '2022', '2026'],
|
|
11
|
+
## default pp opts
|
|
12
|
+
opts: { country: false,
|
|
13
|
+
stadium: false,
|
|
14
|
+
city: true,
|
|
15
|
+
show_stadiums: true,
|
|
16
|
+
show_teams: false,
|
|
17
|
+
timezone: true,
|
|
18
|
+
},
|
|
19
|
+
opts_full: { country: false,
|
|
20
|
+
show_stadiums: true,
|
|
21
|
+
},
|
|
22
|
+
## opts for squads (incl. jersey numbers - yes/no?)
|
|
23
|
+
jerseys: ->(season) { season >= 1954 ? true : false },
|
|
24
|
+
|
|
25
|
+
## outdir = "../../openfootball/worldcup"
|
|
26
|
+
}
|
|
27
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
def assert( test, msg )
|
|
4
|
+
if test
|
|
5
|
+
else
|
|
6
|
+
puts "!! ASSERT FAILED - #{msg}"
|
|
7
|
+
exit 1
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_date_utc( date_str )
|
|
13
|
+
## note - DateTime has NOT daylight saving time (e.g. dst?)
|
|
14
|
+
## or named timezones!!
|
|
15
|
+
## only works with offsets
|
|
16
|
+
##
|
|
17
|
+
## use Time for built-in timezones (and check on daylight saving time etc.)
|
|
18
|
+
|
|
19
|
+
date = DateTime.strptime( date_str, '%Y-%m-%dT%H:%M%z' )
|
|
20
|
+
|
|
21
|
+
assert( date_str == date.strftime('%Y-%m-%dT%H:%MZ'),
|
|
22
|
+
"date parse expected #{date_str} - got #{date.inspect}" )
|
|
23
|
+
date
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def parse_date_local( date_str )
|
|
27
|
+
## fix - parse UTC+-offset !!!!
|
|
28
|
+
## e.g. 2025-08-01 20:30 UTC+2
|
|
29
|
+
date = DateTime.strptime( date_str, '%Y-%m-%d %H:%M UTC%z' )
|
|
30
|
+
|
|
31
|
+
## assert( date_str == date.strftime('%Y-%m-%dT%H:%MZ'),
|
|
32
|
+
## "date parse expected #{date_str} - got #{date.inspect}" )
|
|
33
|
+
date
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
MINUTE_RE = %r{ \A
|
|
39
|
+
(?<minute>\d{1,3}) '?
|
|
40
|
+
( \+
|
|
41
|
+
(?<offset>\d{1,2}) '?
|
|
42
|
+
)?
|
|
43
|
+
\z
|
|
44
|
+
}x
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _parse_minute( str )
|
|
48
|
+
|
|
49
|
+
## support weirdo 120'+-30' -- remove minuts
|
|
50
|
+
str = str.gsub( '-', '' )
|
|
51
|
+
|
|
52
|
+
m = MINUTE_RE.match( str )
|
|
53
|
+
raise ArgumentError, "unknown goal minute format in #{str.inspect}" if m.nil?
|
|
54
|
+
|
|
55
|
+
minute = m[:minute].to_i(10)
|
|
56
|
+
offset = m[:offset] ? m[:offset].to_i(10) : nil
|
|
57
|
+
|
|
58
|
+
[minute,offset]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def _fmt_minute( minute, offset )
|
|
62
|
+
## pp [minute,offset]
|
|
63
|
+
|
|
64
|
+
buf = String.new
|
|
65
|
+
buf << "#{minute}"
|
|
66
|
+
buf << "+#{offset}" if offset
|
|
67
|
+
buf << "'"
|
|
68
|
+
buf
|
|
69
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
|
|
2
|
+
##
|
|
3
|
+
## note:
|
|
4
|
+
## document is container for LeagueSeason holding teams, matches, etc.
|
|
5
|
+
|
|
6
|
+
class Document
|
|
7
|
+
|
|
8
|
+
def self.read( path )
|
|
9
|
+
data = read_json( path )
|
|
10
|
+
new( data )
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize( data )
|
|
14
|
+
@data = data
|
|
15
|
+
|
|
16
|
+
@teams = Teams.new
|
|
17
|
+
@teams.add( data['teams'] )
|
|
18
|
+
## puts " #{teams.size} team(s) in season #{season}"
|
|
19
|
+
|
|
20
|
+
@stadiums = Stadiums.new
|
|
21
|
+
@stadiums.add( data['stadiums'] )
|
|
22
|
+
## puts " #{stadiums.size} stadium(s) in season #{season}"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## read in stages for sorting
|
|
26
|
+
## incl. SequenceOrder, StageLevel (optional)
|
|
27
|
+
## stages = Stages.new
|
|
28
|
+
## stages.add( read_json( "./#{slug}/misc/#{season}_stages.json" )['Results'] )
|
|
29
|
+
|
|
30
|
+
## automagically sort (by groups) - why? why not?
|
|
31
|
+
@data['matches'] = _sort_matches( @data['matches'] )
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def find_team!( q ) @teams.find!( q ); end
|
|
36
|
+
|
|
37
|
+
def find_stadium!( h ) @stadiums.find!( h ); end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## quick ("unsafe/low-level") access for size - keep? why? why not?
|
|
41
|
+
## use team_count, stadium_count, match_count - why? why not?
|
|
42
|
+
def teams() @teams; end
|
|
43
|
+
def stadiums() @stadiums; end
|
|
44
|
+
def matches() @data['matches']; end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def each_match( &blk )
|
|
48
|
+
@data['matches'].each do |_m|
|
|
49
|
+
## use ("typed" struct) wrapper
|
|
50
|
+
m = Match.new( self, _m )
|
|
51
|
+
blk.call( m )
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
alias_method :each, :each_match
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## (was: collect_dates)
|
|
58
|
+
## find a better name e.g. min_max_dates / duration / collect/find_start_end_dates?
|
|
59
|
+
def calc_start_end_dates
|
|
60
|
+
start_date = nil
|
|
61
|
+
end_date = nil
|
|
62
|
+
|
|
63
|
+
@data['matches'].each do |_m|
|
|
64
|
+
|
|
65
|
+
## date_utc = parse_date_utc( _m['datetime_utc'] )
|
|
66
|
+
date_local = parse_date_local( _m['datetime_local'] )
|
|
67
|
+
|
|
68
|
+
## note - alway use local datetime for now
|
|
69
|
+
|
|
70
|
+
if start_date.nil? || date_local < start_date
|
|
71
|
+
start_date = date_local
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if end_date.nil? || date_local > end_date
|
|
75
|
+
end_date = date_local
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
[start_date, end_date]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
###
|
|
85
|
+
## (private) helpers
|
|
86
|
+
|
|
87
|
+
def _sort_matches( matches )
|
|
88
|
+
###
|
|
89
|
+
## sort results by group if present
|
|
90
|
+
|
|
91
|
+
## add "old" sort index
|
|
92
|
+
matches = matches.each_with_index.map {|m,i| m['sort']=i+1; m }
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
matches = matches.sort do |l,r|
|
|
96
|
+
|
|
97
|
+
lhs_stage = l['stage']
|
|
98
|
+
rhs_stage = r['stage']
|
|
99
|
+
|
|
100
|
+
## lhs_stage = stages.find!( lhs_stageName )
|
|
101
|
+
## rhs_stage = stages.find!( rhs_stageName )
|
|
102
|
+
|
|
103
|
+
lhs_group = l['group'] # optional
|
|
104
|
+
rhs_group = r['group'] # optional
|
|
105
|
+
|
|
106
|
+
lhs_matchday = l['matchday'] # optional
|
|
107
|
+
rhs_matchday = r['matchday'] # optional
|
|
108
|
+
|
|
109
|
+
if (lhs_group && rhs_group) && (lhs_stage == rhs_stage)
|
|
110
|
+
res = lhs_group <=> rhs_group
|
|
111
|
+
res = (lhs_matchday && rhs_matchday ?
|
|
112
|
+
lhs_matchday <=> rhs_matchday : 0 ) if res == 0
|
|
113
|
+
## same group; sort by old index (or) date??
|
|
114
|
+
res = l['sort'] <=> r['sort'] if res == 0
|
|
115
|
+
res
|
|
116
|
+
else
|
|
117
|
+
### sort first by stage (seq) and than keep as is
|
|
118
|
+
### res = lhs_stage[:seq] <=> rhs_stage[:seq]
|
|
119
|
+
## res = l['sort'] <=> r['sort'] if res == 0
|
|
120
|
+
## res
|
|
121
|
+
l['sort'] <=> r['sort']
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
matches
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
end # class Document
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class Goal
|
|
4
|
+
def self.build( h )
|
|
5
|
+
## split into minute
|
|
6
|
+
## and offset (stoppage/injury time)
|
|
7
|
+
## e.g. 90'+11'
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
## keep minute as string and incl. stoppage/injury time - why? why not?
|
|
11
|
+
|
|
12
|
+
new( name: h['name'],
|
|
13
|
+
minute: h['minute'],
|
|
14
|
+
og: h['og'], # true|false
|
|
15
|
+
pen: h['pen'] # true|false
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
attr_reader :name,
|
|
21
|
+
:minute, :og, :pen
|
|
22
|
+
|
|
23
|
+
def initialize( name:, minute:,
|
|
24
|
+
og: false, pen: false )
|
|
25
|
+
|
|
26
|
+
norm = norm_name( name )
|
|
27
|
+
if norm != name
|
|
28
|
+
puts " NORM GOAL/PLAYER NAME >#{name}< => >#{norm}<"
|
|
29
|
+
name = norm
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@name = name
|
|
33
|
+
@minute = minute
|
|
34
|
+
|
|
35
|
+
@og, @pen = og, pen
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def og?() @og; end
|
|
39
|
+
def pen?() @pen; end
|
|
40
|
+
|
|
41
|
+
end # class Goal
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
=begin
|
|
48
|
+
## sort by minutes
|
|
49
|
+
## may not be sorted
|
|
50
|
+
|
|
51
|
+
recs = recs.sort do |l,r|
|
|
52
|
+
res = l[:minute] <=> r[:minute]
|
|
53
|
+
res = (l[:offset]||0) <=> (r[:offset]||0) if res == 0 &&
|
|
54
|
+
(l[:minute] == 45 ||
|
|
55
|
+
l[:minute] == 90 ||
|
|
56
|
+
l[:minute] == 105 || ## check - if possible stoppage in 1st half extra-time??
|
|
57
|
+
l[:minute] == 120)
|
|
58
|
+
res
|
|
59
|
+
end
|
|
60
|
+
=end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class Match
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
## use Match.build( h ) - why? why not?
|
|
8
|
+
|
|
9
|
+
attr_reader :team1, :team2,
|
|
10
|
+
:score,
|
|
11
|
+
:date_utc, :date_local, :utc_offset,
|
|
12
|
+
:stadium,
|
|
13
|
+
:goals1, :goals2
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def initialize( doc, data )
|
|
18
|
+
## expect a data as (json) hash for now
|
|
19
|
+
@doc = doc
|
|
20
|
+
@h = data
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## note - always lookup full team records (use match inline only as refs)
|
|
24
|
+
@team1 = @doc.find_team!( @h['team1'] )
|
|
25
|
+
@team2 = @doc.find_team!( @h['team2'] )
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@date_utc = parse_date_utc( @h['datetime_utc'] )
|
|
29
|
+
@date_local = parse_date_local( @h['datetime_local'] )
|
|
30
|
+
|
|
31
|
+
assert( @date_utc.sec == 0 && @date_local.sec == 0,
|
|
32
|
+
"sec 00 expected" )
|
|
33
|
+
|
|
34
|
+
## note: returns Rational (e.g. 3/1 or 1/4 etc.) use to_f/to_i to convert
|
|
35
|
+
## diff_in_hours = ((localDateTime - dateTime) * 24).to_f
|
|
36
|
+
## diff_in_days = localDateTime.jd - dateTime.jd
|
|
37
|
+
|
|
38
|
+
# note - offset is in rational fraction of a day (e.g. 1/12 for 2hours)
|
|
39
|
+
## was diff_in_hours
|
|
40
|
+
@utc_offset = (@date_local.offset * 24).to_i
|
|
41
|
+
|
|
42
|
+
## pp [@date_utc, @date_local, @utc_offset]
|
|
43
|
+
|
|
44
|
+
## note - always lookup full stadium record (use match inline only as ref)
|
|
45
|
+
@stadium = @doc.find_stadium!( @h['stadium'] )
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@score = @h['score'] ? Score.build( @h['score'] ) : nil
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@goals1 = @h['goals1'] ? @h['goals1'].map { |h| Goal.build( h ) } : nil
|
|
52
|
+
@goals2 = @h['goals2'] ? @h['goals2'].map { |h| Goal.build( h ) } : nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## use _data to mark as "private" and do NOT use - why? why not?
|
|
58
|
+
def data() @h; end
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## todo - find a better name for timezone offset
|
|
63
|
+
## use utc_offset - why? why not?
|
|
64
|
+
alias_method :diff_in_hours, :utc_offset
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def stage() @h['stage']; end
|
|
68
|
+
def group() @h['group']; end # optional
|
|
69
|
+
def num() @h['number']; end # optional (match) number
|
|
70
|
+
def matchday() @h['matchday']; end # optional
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def attendance() @h['attendance']; end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
end ## class Match
|