openliga 0.0.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 +7 -0
- data/CHANGELOG.md +4 -0
- data/Manifest.txt +18 -0
- data/README.md +225 -0
- data/Rakefile +33 -0
- data/bin/openliga +14 -0
- data/config/leagues_de.csv +65 -0
- data/config/leagues_more.csv +43 -0
- data/lib/openliga/convert-goals.rb +62 -0
- data/lib/openliga/convert-match.rb +94 -0
- data/lib/openliga/convert-score.rb +172 -0
- data/lib/openliga/convert.rb +68 -0
- data/lib/openliga/convert_helpers.rb +73 -0
- data/lib/openliga/download.rb +81 -0
- data/lib/openliga/leagues.rb +45 -0
- data/lib/openliga/models.rb +153 -0
- data/lib/openliga/pp_matches.rb +146 -0
- data/lib/openliga/tool.rb +138 -0
- data/lib/openliga.rb +26 -0
- metadata +142 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
module Openliga
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def self.build_score( h )
|
|
6
|
+
ht = nil
|
|
7
|
+
ft = nil
|
|
8
|
+
et = nil
|
|
9
|
+
pen = nil
|
|
10
|
+
|
|
11
|
+
maybe_ft = nil
|
|
12
|
+
maybe_et = nil
|
|
13
|
+
|
|
14
|
+
h.each do |result|
|
|
15
|
+
## note - assume extra time for both options now
|
|
16
|
+
## assert ft is nil - why? why not?
|
|
17
|
+
|
|
18
|
+
### resultName check - why? why not?
|
|
19
|
+
### are legacy or not?
|
|
20
|
+
|
|
21
|
+
###
|
|
22
|
+
## todo - check copa america
|
|
23
|
+
## if extra time excluded on penalties???
|
|
24
|
+
|
|
25
|
+
case result['resultTypeKind']
|
|
26
|
+
when 'HalfTime'
|
|
27
|
+
## result['resultName'] == 'Halbzeitergebnis' ||
|
|
28
|
+
## result['resultName'] == 'Halbzeit'
|
|
29
|
+
## desc = >Ergebnis nach Ende der ersten Halbzeit
|
|
30
|
+
ht = [
|
|
31
|
+
result['pointsTeam1'],
|
|
32
|
+
result['pointsTeam2'],
|
|
33
|
+
]
|
|
34
|
+
##
|
|
35
|
+
## note if resultName is Endergebnis this might actually
|
|
36
|
+
## be after penalty shootout or extra time!!!
|
|
37
|
+
## check if "resultName": "nach 90 Minuten"
|
|
38
|
+
## is present too!!
|
|
39
|
+
when 'After90Minutes'
|
|
40
|
+
## result['resultName'] == 'Endergebnis' ## ||
|
|
41
|
+
## result['resultName'] == 'nach Nachspielzeit' ## !!!!
|
|
42
|
+
## desc => Ergebnis nach Ende der offiziellen Spielzeit
|
|
43
|
+
ft = [
|
|
44
|
+
result['pointsTeam1'],
|
|
45
|
+
result['pointsTeam2'],
|
|
46
|
+
]
|
|
47
|
+
when 'AfterExtraTime'
|
|
48
|
+
## result['resultName'] == 'Verlängerung' ||
|
|
49
|
+
## result['resultName'] == 'nach Verlängerung'
|
|
50
|
+
## desc: Ergebnis nach Verlängerung
|
|
51
|
+
et = [
|
|
52
|
+
result['pointsTeam1'],
|
|
53
|
+
result['pointsTeam2'],
|
|
54
|
+
]
|
|
55
|
+
when 'AfterPenalties'
|
|
56
|
+
## result['resultName'] == 'Elfmeterschießen' ||
|
|
57
|
+
## result['resultName'] == 'nach Elfmeterschießen'
|
|
58
|
+
## desc: Ergebnis nach Elfmeterschießen
|
|
59
|
+
pen = [
|
|
60
|
+
result['pointsTeam1'],
|
|
61
|
+
result['pointsTeam2'],
|
|
62
|
+
]
|
|
63
|
+
when 'Unknown'
|
|
64
|
+
### note:
|
|
65
|
+
## for now resultName: "nach 90 Minutes" overwrites
|
|
66
|
+
## first After90Minutes!!! (assuming this is kind of Endergebnis really incl. extra-time or penalty shootout)
|
|
67
|
+
if result['resultName'] == 'nach 90 Minuten'
|
|
68
|
+
puts "!! warn - weirdo Unknown/nach 90 Minuten result found"
|
|
69
|
+
ft = [
|
|
70
|
+
result['pointsTeam1'],
|
|
71
|
+
result['pointsTeam2'],
|
|
72
|
+
]
|
|
73
|
+
elsif result['resultName'] == 'Endergebnis'
|
|
74
|
+
puts "!! warn - weirdo Unknown/Endergebnis result found"
|
|
75
|
+
maybe_ft = [
|
|
76
|
+
result['pointsTeam1'],
|
|
77
|
+
result['pointsTeam2'],
|
|
78
|
+
]
|
|
79
|
+
elsif result['resultName'] == 'Nachspielzeit'
|
|
80
|
+
puts "!! warn - weirdo Unknown/Nachspielzeit result found"
|
|
81
|
+
maybe_et = [
|
|
82
|
+
result['pointsTeam1'],
|
|
83
|
+
result['pointsTeam2'],
|
|
84
|
+
]
|
|
85
|
+
else
|
|
86
|
+
puts "!! ERROR - unknown <Unknown> result type:"
|
|
87
|
+
pp result
|
|
88
|
+
exit 1
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
puts "!! ERROR - unknown result type:"
|
|
92
|
+
pp result
|
|
93
|
+
exit 1
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
## note - DFB Pokal 2025/2026
|
|
98
|
+
### requires hack
|
|
99
|
+
## if Nachspielzeit (et) present
|
|
100
|
+
## only accept if different from ft or pen is present!!!
|
|
101
|
+
|
|
102
|
+
if maybe_et
|
|
103
|
+
if pen || maybe_et != ft
|
|
104
|
+
et = maybe_et
|
|
105
|
+
else
|
|
106
|
+
puts "!! WARN - ignoring et (Unknown/Nachspielzeit) result in:"
|
|
107
|
+
pp h
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
if maybe_ft
|
|
112
|
+
if ft.nil?
|
|
113
|
+
ft = maybe_ft
|
|
114
|
+
else
|
|
115
|
+
puts "!! WARN - ignoring ft (Unknown/Endergebnis) result in:"
|
|
116
|
+
pp h
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
Score.new( ht: ht, ft: ft, et: et, pen: pen )
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
end ## module Openliga
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
=begin
|
|
129
|
+
"matchResults": [
|
|
130
|
+
{
|
|
131
|
+
"resultID": 110926,
|
|
132
|
+
"resultName": "Halbzeitergebnis",
|
|
133
|
+
"pointsTeam1": 3,
|
|
134
|
+
"pointsTeam2": 0,
|
|
135
|
+
"resultOrderID": 1,
|
|
136
|
+
"resultTypeID": 1,
|
|
137
|
+
"resultDescription": "Ergebnis zur Halbzeitpause"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"resultID": 110927,
|
|
141
|
+
"resultName": "Endergebnis",
|
|
142
|
+
"pointsTeam1": 5,
|
|
143
|
+
"pointsTeam2": 1,
|
|
144
|
+
"resultOrderID": 2,
|
|
145
|
+
"resultTypeID": 2,
|
|
146
|
+
"resultDescription": "Ergebnis nach Ende der offiziellen Spielzeit"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
{"resultID"=>127243,
|
|
150
|
+
"resultName"=>"Halbzeit",
|
|
151
|
+
"pointsTeam1"=>0,
|
|
152
|
+
"pointsTeam2"=>10,
|
|
153
|
+
"resultOrderID"=>1,
|
|
154
|
+
"resultTypeID"=>1,
|
|
155
|
+
"resultTypeKind"=>"HalfTime",
|
|
156
|
+
"resultDescription"=>"Ergebnis nach Ende der ersten Halbzeit"}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
or
|
|
160
|
+
!! ERROR - unknown result type:
|
|
161
|
+
{"resultID"=>118424,
|
|
162
|
+
"resultName"=>"Nachspielzeit",
|
|
163
|
+
"pointsTeam1"=>1,
|
|
164
|
+
"pointsTeam2"=>2,
|
|
165
|
+
"resultOrderID"=>3,
|
|
166
|
+
"resultTypeID"=>3,
|
|
167
|
+
"resultTypeKind"=>"Unknown",
|
|
168
|
+
"resultDescription"=>"Ergebnis nach Nachspielzeit"}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
DFB Pokal 2025/2026
|
|
172
|
+
=end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Openliga
|
|
2
|
+
|
|
3
|
+
def self.convert( league:, season: )
|
|
4
|
+
|
|
5
|
+
season = Season( season ) ## cast (ensure) season class (NOT string, integer, etc.)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
info = (LEAGUES[ league.downcase ]||{})[ season.to_key ]
|
|
9
|
+
if info.nil?
|
|
10
|
+
puts "!! ERROR - no openliga info found for #{league} #{season}"
|
|
11
|
+
exit 1
|
|
12
|
+
end
|
|
13
|
+
pp info
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
data = Webcache.read_json( Metal.matches_url( info.shortcut, info.season ))
|
|
17
|
+
puts " #{data.size} match(es)"
|
|
18
|
+
|
|
19
|
+
## data_teams = Webcache.read_json( Metal.teams_url( LEAGUES[league.downcase], season.start_year ))
|
|
20
|
+
## puts " #{data_teams.size} team(s)"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
body = _convert( data )
|
|
24
|
+
|
|
25
|
+
###
|
|
26
|
+
### auto-add header
|
|
27
|
+
header =<<TXT
|
|
28
|
+
###
|
|
29
|
+
# converted from openligadb.de json to Football.TXT
|
|
30
|
+
# for source, see https://api.openligadb.de/getmatchdata/#{info.shortcut}/#{info.season}
|
|
31
|
+
|
|
32
|
+
= #{info.name}
|
|
33
|
+
|
|
34
|
+
TXT
|
|
35
|
+
|
|
36
|
+
header + body
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def self._convert( data )
|
|
42
|
+
|
|
43
|
+
recs = []
|
|
44
|
+
|
|
45
|
+
matches = data
|
|
46
|
+
matches.each do |h|
|
|
47
|
+
m = build_match( h )
|
|
48
|
+
|
|
49
|
+
## note - if match is broken e.g. has no team1 or team2
|
|
50
|
+
## than skip for now
|
|
51
|
+
|
|
52
|
+
if m.team1.nil? || m.team2.nil?
|
|
53
|
+
puts "!! warn - skipping match with team missing:"
|
|
54
|
+
pp h
|
|
55
|
+
else
|
|
56
|
+
recs << m
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
## pp recs
|
|
61
|
+
|
|
62
|
+
pp_matches( recs )
|
|
63
|
+
end # method self._convert
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
end # module Openliga
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Openliga
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def self._clean( str )
|
|
5
|
+
## note - accept nil
|
|
6
|
+
## turn blank strings e.g "" or " " into nil
|
|
7
|
+
## remove leading and trailing spaces
|
|
8
|
+
return nil if str.nil?
|
|
9
|
+
|
|
10
|
+
str = str.strip
|
|
11
|
+
if str.empty?
|
|
12
|
+
nil
|
|
13
|
+
else
|
|
14
|
+
str
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
=begin
|
|
19
|
+
@ Sportpark Ronhof | Thomas Sommer, Fürth
|
|
20
|
+
@ Sportforum "Sojus 31", Zwickau
|
|
21
|
+
@ "Allianz Arena, München
|
|
22
|
+
|
|
23
|
+
or add into protected_name token - why? why not?
|
|
24
|
+
e.g.
|
|
25
|
+
@ ‹Sportpark Ronhof | Thomas Sommer›, Fürth
|
|
26
|
+
@ ‹Sportforum "Sojus 31"›, Zwickau
|
|
27
|
+
@ ‹"Allianz Arena›, München -- really better FIX typo
|
|
28
|
+
=end
|
|
29
|
+
|
|
30
|
+
def self._clean_geo( str )
|
|
31
|
+
return nil if str.nil?
|
|
32
|
+
|
|
33
|
+
str = str.gsub( %r{["|]}, '' ) ## remove "|
|
|
34
|
+
str = str.gsub( %r{[ ]{2,}}, ' ' ) ## squish spaces (maybe move "upstream" to clean)
|
|
35
|
+
|
|
36
|
+
_clean( str )
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def self._clean_name( str ) ## player name
|
|
41
|
+
## e.g.
|
|
42
|
+
## Poulsen, Yussuf => Poulsen Yussuf
|
|
43
|
+
## Günther-Schmidt, Julian
|
|
44
|
+
## Hercher, Philipp
|
|
45
|
+
## maybe auto-turn around later - why? why not?
|
|
46
|
+
##
|
|
47
|
+
## E. N‘Dicka => E. N'Dicka
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
return nil if str.nil?
|
|
51
|
+
|
|
52
|
+
## move asciify "upstream" - why? why not?
|
|
53
|
+
str = str.gsub( /[‘]/, "'" ) ## asciify "smart/unicode" quotes
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## str = str.gsub( /[,]/, '' ) ## remove "," -- todo - add a warn(ing) on replace
|
|
57
|
+
## tr = str.sub( 'Poulsen, Yussuf', 'Yussuf Poulsen' )
|
|
58
|
+
##
|
|
59
|
+
## turn "Poulsen, Yussuf" into "Yussuf Poulsen"
|
|
60
|
+
if str.include?(',')
|
|
61
|
+
last_name, first_name = str.split( ',', 2 )
|
|
62
|
+
str = "#{first_name} #{last_name}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
str = str.gsub( /[ ]{2,}/, ' ' ) ## squish spaces (maybe move "upstream" to clean)
|
|
67
|
+
|
|
68
|
+
_clean( str )
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
end ## module Openliga
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Openliga
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def self.download( league:, season: )
|
|
5
|
+
|
|
6
|
+
season = Season( season ) ## cast (ensure) season class (NOT string, integer, etc.)
|
|
7
|
+
|
|
8
|
+
info = (LEAGUES[ league.downcase ]||{})[ season.to_key ]
|
|
9
|
+
if info.nil?
|
|
10
|
+
puts "!! ERROR - no openliga info found for #{league} #{season}"
|
|
11
|
+
exit 1
|
|
12
|
+
end
|
|
13
|
+
pp info
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
Metal.matches( info.shortcut, info.season )
|
|
17
|
+
Metal.teams( info.shortcut, info.season )
|
|
18
|
+
|
|
19
|
+
true ## return true on success (instead of latest response json data/payload)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
##################
|
|
24
|
+
## plumbing metal "helpers"
|
|
25
|
+
|
|
26
|
+
class Metal
|
|
27
|
+
|
|
28
|
+
BASE_URL = 'https://api.openligadb.de'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### api call - /getavailableleagues
|
|
32
|
+
def self.leagues
|
|
33
|
+
get( "#{BASE_URL}/getavailableleagues" )
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
## api call - /getavailableteams/{code}/{year}
|
|
37
|
+
def self.teams( code, year )
|
|
38
|
+
get( teams_url( code, year ) )
|
|
39
|
+
end
|
|
40
|
+
def self.teams_url( code, year )
|
|
41
|
+
"#{BASE_URL}/getavailableteams/#{code}/#{year}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
## api call - /getmatchdata/{code}/{year}
|
|
45
|
+
def self.matches( code, year )
|
|
46
|
+
get( matches_url( code, year ) )
|
|
47
|
+
end
|
|
48
|
+
def self.matches_url( code, year )
|
|
49
|
+
"#{BASE_URL}/getmatchdata/#{code}/#{year}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
## api call - /getgoalgetters/{code}/{year}
|
|
53
|
+
def self.goalgetters( code, year )
|
|
54
|
+
get( "#{BASE_URL}/getgoalgetters/#{code}/#{year}" )
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def self.get( url )
|
|
63
|
+
headers = {}
|
|
64
|
+
headers['User-Agent'] = 'ruby'
|
|
65
|
+
headers['Accept'] = '*/*'
|
|
66
|
+
|
|
67
|
+
## note: add format: 'json' for pretty printing json (before) save in cache
|
|
68
|
+
response = Webget.call( url, headers: headers )
|
|
69
|
+
|
|
70
|
+
exit 1 if response.status.nok? # e.g. HTTP status code != 200
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
data = response.json
|
|
74
|
+
## for debugging print pretty printed json first 400 chars
|
|
75
|
+
puts data.pretty_inspect[0..400]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
data ## note - return json data response (on success)
|
|
79
|
+
end
|
|
80
|
+
end # class Metal
|
|
81
|
+
end # module Footballdata
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Openliga
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def self.config_dir() "#{File.dirname(File.dirname(__dir__))}/config"; end
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## code, season, league_name, league_season, league_shortcut
|
|
8
|
+
##
|
|
9
|
+
## "leagueName": "Champions League 2026/2027",
|
|
10
|
+
## "leagueShortcut": "ucl",
|
|
11
|
+
## "leagueSeason": 2026,
|
|
12
|
+
##
|
|
13
|
+
## from https://api.openligadb.de
|
|
14
|
+
## Name Description
|
|
15
|
+
## leagueShortcut - string - der Shortcut der Liga, z.B. 'bl1' für die erste Bundesliga
|
|
16
|
+
## leagueSeason - integer($int32) - die Saison der Liga, z.B. 2019 für die Saison 2019/2020
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
League = Struct.new( :name, :season, :shortcut)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
LEAGUES = begin
|
|
26
|
+
leagues = {}
|
|
27
|
+
['leagues_de.csv',
|
|
28
|
+
'leagues_more.csv'].each do |name|
|
|
29
|
+
path = "#{config_dir}/#{name}"
|
|
30
|
+
rows = read_csv( path )
|
|
31
|
+
## pp rows
|
|
32
|
+
rows.each do |row|
|
|
33
|
+
seasons = leagues[row['code']] ||= {}
|
|
34
|
+
## note - normalize season!!
|
|
35
|
+
season = Season.parse( row['season'] )
|
|
36
|
+
seasons[ season.to_key ] = League.new( name: row['league_name'],
|
|
37
|
+
season: row['league_season'].to_i(10),
|
|
38
|
+
shortcut: row['league_shortcut'] )
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
leagues
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
end # module Openliga
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
module Openliga
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
Match = Struct.new( :team1,
|
|
5
|
+
:team2,
|
|
6
|
+
:date, # assumes Date class
|
|
7
|
+
:time, # assumes String class (00:00)
|
|
8
|
+
:timezone, ## use tz ?
|
|
9
|
+
### :datetime_utc,
|
|
10
|
+
:score,
|
|
11
|
+
:goals,
|
|
12
|
+
:round, ## is stage/round/group
|
|
13
|
+
:city,
|
|
14
|
+
:ground, ## use stadium (or venue?)
|
|
15
|
+
:att, ## attendance (numberOfViewers)
|
|
16
|
+
:finished ### todo/check - use status (is really only END or false)
|
|
17
|
+
) do
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
Goal = Struct.new( :team, ## expect 1|2 or nil
|
|
24
|
+
:name,
|
|
25
|
+
:score, ## expect array e.g. [0,1]
|
|
26
|
+
:m, :stoppage,
|
|
27
|
+
:pen, :og,
|
|
28
|
+
:comment ) do
|
|
29
|
+
|
|
30
|
+
def pen?() @pen == true; end
|
|
31
|
+
def og?() @og == true; end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## print goal as single line
|
|
35
|
+
## e.g.
|
|
36
|
+
## (0-1 Yorbe Vertessen 37'
|
|
37
|
+
## 0-2 Yorbe Vertessen 48')
|
|
38
|
+
|
|
39
|
+
def calc_stoppage( m )
|
|
40
|
+
if m >= 120 then [120, m%120]
|
|
41
|
+
elsif m >= 105 then [105, m%105]
|
|
42
|
+
elsif m >= 90 then [90, m%90]
|
|
43
|
+
else [45, m%45] ## assume 45
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def to_s
|
|
50
|
+
buf = String.new
|
|
51
|
+
|
|
52
|
+
## score required !!!
|
|
53
|
+
buf << "#{score[0]}-#{score[1]} " ## note - use double (two) space for now!!
|
|
54
|
+
buf << (name ? name : 'N.N.') ## note - if no name use N.N. for now!!
|
|
55
|
+
if m
|
|
56
|
+
if stoppage
|
|
57
|
+
_m,_stoppage = calc_stoppage(m)
|
|
58
|
+
puts "!! INFO - auto-calc stoppage #{m}+ => #{_m}+#{_stoppage}"
|
|
59
|
+
buf << " #{_m}+#{_stoppage}'"
|
|
60
|
+
else
|
|
61
|
+
buf << " #{m}'"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
buf << " (og)" if og
|
|
66
|
+
buf << " (p)" if pen
|
|
67
|
+
|
|
68
|
+
buf
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
######
|
|
76
|
+
## todo/fix - reuse a "generic" score (and/or result) class
|
|
77
|
+
|
|
78
|
+
class Score
|
|
79
|
+
##
|
|
80
|
+
## for generic add add reported (score) too
|
|
81
|
+
## use if score line undefined/unknown
|
|
82
|
+
|
|
83
|
+
attr_reader :ht, :ft, :et, :pen
|
|
84
|
+
|
|
85
|
+
def initialize( ht: [], ft:[], et: [], pen: [])
|
|
86
|
+
@ht, @ft, @et, @pen = ht, ft, et, pen
|
|
87
|
+
|
|
88
|
+
## ( ..., extra_time: nil)
|
|
89
|
+
## extra_time flag - e.g. copa america has penalty shootouts
|
|
90
|
+
## BUT no extra time (only in final)
|
|
91
|
+
##
|
|
92
|
+
## add a reg (90min) flag too -
|
|
93
|
+
## @extra_time = extra_time
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def pen?() @pen.is_a?( Array ) && @pen.size == 2; end
|
|
98
|
+
def et?() @et.is_a?( Array ) && @et.size == 2; end
|
|
99
|
+
def ft?() @ft.is_a?( Array ) && @ft.size == 2; end
|
|
100
|
+
def ht?() @ht.is_a?( Array ) && @ht.size == 2; end
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def pretty_print( q )
|
|
104
|
+
q.group(1, "<Score", ">") do
|
|
105
|
+
q.text( " ht: #{@ht}") if ht?
|
|
106
|
+
q.text( " ft: #{@ft}") if ft?
|
|
107
|
+
q.text( " et: #{@et}") if et?
|
|
108
|
+
q.text( " pen: #{@pen}") if pen?
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def to_s
|
|
115
|
+
buf = String.new
|
|
116
|
+
if pen?
|
|
117
|
+
if et?
|
|
118
|
+
buf << "#{@et[0]}-#{@et[1]} aet"
|
|
119
|
+
if ft?
|
|
120
|
+
buf << " (#{@ft[0]}-#{@ft[1]}"
|
|
121
|
+
buf << ", #{@ht[0]}-#{@ht[1]}" if ht?
|
|
122
|
+
buf << ")"
|
|
123
|
+
end
|
|
124
|
+
elsif ft?
|
|
125
|
+
### pen without et e.g. in copa
|
|
126
|
+
## use 2-2 (1-1), 5-3 pen. or such
|
|
127
|
+
buf << " " unless buf.empty?
|
|
128
|
+
buf << "#{@ft[0]}-#{@ft[1]}"
|
|
129
|
+
buf << " (#{@ht[0]}-#{@ht[1]})" if ht?
|
|
130
|
+
end
|
|
131
|
+
buf << ", " unless buf.empty?
|
|
132
|
+
buf << "#{@pen[0]}-#{@pen[1]} pen"
|
|
133
|
+
elsif et?
|
|
134
|
+
buf << "#{@et[0]}-#{@et[1]} aet"
|
|
135
|
+
if ft?
|
|
136
|
+
buf << " (#{@ft[0]}-#{@ft[1]}"
|
|
137
|
+
buf << ", #{@ht[0]}-#{@ht[1]}" if ht?
|
|
138
|
+
buf << ")"
|
|
139
|
+
end
|
|
140
|
+
elsif ft?
|
|
141
|
+
buf << "#{@ft[0]}-#{@ft[1]}"
|
|
142
|
+
buf << " (#{@ht[0]}-#{@ht[1]})" if ht?
|
|
143
|
+
else
|
|
144
|
+
## note - return empty string on no score for now
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
buf
|
|
148
|
+
end
|
|
149
|
+
end # class Score
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
end ## module Openliga
|