Class: ICU::Tournament::SPExport
- Inherits:
-
Object
- Object
- ICU::Tournament::SPExport
- Defined in:
- lib/icu_tournament/tournament_spx.rb
Overview
The SWissPerfect export format used to be important in Irish chess as it was used to submit results to the ICU’s first computerised ratings system, a MicroSoft Access database. As a text based format, it was easier to manipulate than the full binary formats of SwissPerfect. Here is an illustrative example of this format:
No Name Feder Intl Id Loc Id Rtg Loc Title Total 1 2 3
1 Duck, Daffy IRL 12345 2200 im 2 0:= 3:W 2:D
2 Mouse, Minerva 1234568 1900 1.5 3:D 0:= 1:D
3 Mouse, Mickey USA 1234567 gm 1 2:D 1:L 0:=
The format does not record either the name nor the start date of the tournament. Player colours are also missing. When parsing data in this format it is necessary to specify name and start date explicitly:
parser = ICU::Tournament::SPExport.new
tournament = parser.parse_file('sample.txt', :name => 'Mickey Mouse Masters', :start => '2011-02-06')
tournament.name # => "Mickey Mouse Masters"
tournament.start # => "2011-02-06"
tournament.rounds # => 3
tournament.player(1).name # => "Duck, Daffy"
tournament.player(2).points # => 1.5
tournament.player(3).fed # => "USA"
See ICU::Tournament for further details about the object returned.
The SwissPerfect application offers a number of choices when exporting a tournament cross table, one of which is the column separator. The ICU::Tournament::SPExport parser can only handle data with tab separators but is able to cope with any other configuration choices. For example, if some of the optional columns are missing or if the data is not formatted with space padding.
To serialize an ICU::Tournament instance to the format, use the serialize method of the appropriate parser:
parser = ICU::Tournament::Krause.new
spexport = parser.serialize(tournament)
or use the serialize method of the instance with the appropraie format name:
spexport = tournament.serialize('SPExport')
In either case the method returns a string representation of the tourament in SwissPerfect export format with tab separators, space padding and (by default) all the available information about the players. To customize what is displayed, use the only option and supply an array of symbols or strings to specify which columns to include. For example:
spexport = tournament.serialize('SPExport', :only => [:id, :points])
No Name Loc Id Total 1 2 3
1 Griffiths, Ryan-Rhys 6897 3 4:W 2:W 3:W
2 Flynn, Jamie 5226 2 3:W 1:L 4:W
3 Hulleman, Leon 6409 1 2:L 4:W 1:L
4 Dunne, Thomas 10914 0 1:L 3:L 2:L
The optional attribute names, together with their column header names in SwissPerfect, are as follows: fed (Feder), fide_id (Intl Id), id (Loc Id), fide_rating (Rtg), rating (Loc), title (Title), points: (Total). To omitt the optional columns completely, supply an empty array of column names:
tournament.serialize('SPExport', :only => [])
No Name 1 2 3
1 Griffiths, Ryan-Rhys 4:W 2:W 3:W
2 Flynn, Jamie 3:W 1:L 4:W
3 Hulleman, Leon 2:L 4:W 1:L
4 Dunne, Thomas 1:L 3:L 2:L
Or supply whatever columns you want, for example:
tournament.serialize('SPExport', :only => %w{fide_id fide_rating})
Or to omitt rather than include, use the logically opposite except option:
tournament.serialize('SPExport', :except => [:fide_id, :fide_rating])
Note that the column order in the serialised string is the same as it is in the SwissPerfect application. The order of column names in the only option has no effect.
The default, when you leave out the only or except options, is equivalent to both of the following:
tournament.serialize('SPExport', :only => %w{fed fide_id id fide_rating rating title points})
tournament.serialize('SPExport', :except => [])
The order of players in the serialized output is always by player number and as a side effect of serialization, the player numbers will be adjusted to ensure they range from 1 to the total number of players, maintaining the original order. If you would prefer rank-order instead, then you must first renumber the players by rank before serializing. For example:
spexport = tournament.renumber(:rank).serialize('SPExport')
Or equivalently, since renumbering by rank is the default, just:
spexport = tournament.renumber.serialize('SPExport')
You may wish to set the tie-break rules before ranking:
tournament.tie_breaks = [:buchholz, :neustadtl]
spexport = tournament.rerank.renumber.serialize('SwissPerfect')
See ICU::Tournament for more about tie-breaks.
Constant Summary collapse
- COLUMNS =
[ [:num, "No"], [:name, "Name"], [:fed, "Feder"], [:fide_id, "Intl Id"], [:id, "Loc Id"], [:fide_rating, "Rtg"], [:rating, "Loc"], [:title, "Title"], [:points, "Total"], ]
- KEY2NAM =
COLUMNS.inject({}) { |h,c| h[c.first] = c.last; h }
- NAM2KEY =
COLUMNS.inject({}) { |h,c| h[c.last] = c.first; h }
Instance Attribute Summary collapse
-
#error ⇒ Object
readonly
Returns the value of attribute error.
Instance Method Summary collapse
-
#parse(spx, arg = {}) ⇒ Object
Parse SwissPerfect export text returning a Tournament on success or a nil on failure.
-
#parse!(spx, arg = {}) ⇒ Object
Parse SwissPerfect export data returning a Tournament on success or raising an exception on error.
-
#parse_file(file, arg = {}) ⇒ Object
Same as parse except the input is a file name rather than file contents.
-
#parse_file!(file, arg = {}) ⇒ Object
Same as parse! except the input is a file name rather than file contents.
-
#serialize(t, arg = {}) ⇒ Object
Serialise a tournament to SwissPerfect text export format.
-
#validate!(t) ⇒ Object
Additional tournament validation rules for this specific type.
Instance Attribute Details
#error ⇒ Object (readonly)
Returns the value of attribute error.
108 109 110 |
# File 'lib/icu_tournament/tournament_spx.rb', line 108 def error @error end |
Instance Method Details
#parse(spx, arg = {}) ⇒ Object
Parse SwissPerfect export text returning a Tournament on success or a nil on failure. In the case of failure, an error message can be retrived via the error method.
164 165 166 167 168 169 170 171 |
# File 'lib/icu_tournament/tournament_spx.rb', line 164 def parse(spx, arg={}) begin parse!(spx, arg) rescue => ex @error = ex. nil end end |
#parse!(spx, arg = {}) ⇒ Object
Parse SwissPerfect export data returning a Tournament on success or raising an exception on error.
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/icu_tournament/tournament_spx.rb', line 126 def parse!(spx, arg={}) @tournament = init_tournament(arg) @lineno = 0 @header = nil @results = Array.new spx = ICU::Util::String.to_utf8(spx) unless arg[:is_utf8] # Process each line. spx.each_line do |line| @lineno += 1 line.strip! # remove leading and trailing white space next if line == '' # skip blank lines if @header process_player(line) else process_header(line) end end # Now that all players are present, add the results to the tournament. @results.each do |r| lineno, player, data, result = r begin @tournament.add_result(result) rescue => err raise "line #{lineno}, player #{player}, result '#{data}': #{err.}" end end # Finally, exercise the tournament object's internal validation, reranking if neccessary. @tournament.validate!(:rerank => true) @tournament end |
#parse_file(file, arg = {}) ⇒ Object
Same as parse except the input is a file name rather than file contents.
181 182 183 184 185 186 187 188 |
# File 'lib/icu_tournament/tournament_spx.rb', line 181 def parse_file(file, arg={}) begin parse_file!(file, arg) rescue => ex @error = ex. nil end end |
#parse_file!(file, arg = {}) ⇒ Object
Same as parse! except the input is a file name rather than file contents.
174 175 176 177 178 |
# File 'lib/icu_tournament/tournament_spx.rb', line 174 def parse_file!(file, arg={}) spx = ICU::Util::File.read_utf8(file) arg[:is_utf8] = true parse!(spx, arg) end |
#serialize(t, arg = {}) ⇒ Object
Serialise a tournament to SwissPerfect text export format.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/icu_tournament/tournament_spx.rb', line 191 def serialize(t, arg={}) t.validate!(:type => self) # Ensure a nice set of player numbers and get the number of rounds. t.renumber(:order) rounds = t.last_round # Optional columns. defaults = COLUMNS.map(&:first) case when arg[:except].instance_of?(Array) optional = (Set.new(defaults) - arg[:except].map!(&:to_s).map!(&:to_sym)).to_a when arg[:only].instance_of?(Array) optional = arg[:only].map!(&:to_s).map!(&:to_sym) else optional = defaults end optional = optional.inject({}) { |m, a| m[a] = true; m } # Columns identifiers in SwissPerfect order. columns = Array.new columns.push(:num) columns.push(:name) defaults.each { |x| columns.push(x) if optional[x] && x != :num && x != :name } # Widths and formats for each column. width = Hash.new format = Hash.new columns.each do |col| width[col] = t.players.inject(KEY2NAM[col].length) { |l, p| p.send(col).to_s.length > l ? p.send(col).to_s.length : l } format[col] = "%-#{width[col]}s" end # The header, followed by a blank line. formats = columns.map{ |col| format[col] } (1..rounds).each { |r| formats << "%#{width[:num]}d " % r } sp = formats.join("\t") % columns.map{ |col| KEY2NAM[col] } sp << "\r\n\r\n" # The round formats for players are slightly different to those for the header. formats.pop(rounds) (1..rounds).each{ |r| formats << "%#{2+width[:num]}s" } # Serialize the formats already. formats = formats.join("\t") + "\r\n" # Now add a line for each player. t.players.each { |p| sp << p.to_sp_text(rounds, columns, formats) } # And return the whole lot. sp end |
#validate!(t) ⇒ Object
Additional tournament validation rules for this specific type.
245 246 247 |
# File 'lib/icu_tournament/tournament_spx.rb', line 245 def validate!(t) # None. end |