Class: SportDb::MatchTree
- Inherits:
-
Object
- Object
- SportDb::MatchTree
- Includes:
- LogUtils::Logging
- Defined in:
- lib/sportdb/quick/match_tree.rb,
lib/sportdb/quick/match_tree/goal.rb,
lib/sportdb/quick/match_tree/group.rb,
lib/sportdb/quick/match_tree/match.rb,
lib/sportdb/quick/match_tree/round.rb,
lib/sportdb/quick/match_tree-helpers.rb,
lib/sportdb/quick/match_tree_on/on_goal_line.rb,
lib/sportdb/quick/match_tree_on/on_group_def.rb,
lib/sportdb/quick/match_tree_on/on_round_def.rb,
lib/sportdb/quick/match_tree_on/on_match_line.rb,
lib/sportdb/quick/match_tree_on/on_date_header.rb,
lib/sportdb/quick/match_tree_on/on_round_outline.rb
Overview
simple (match) parse tree to structs walker/handler/converter
Defined Under Namespace
Classes: Goal, Group, Match, Round
Constant Summary collapse
- GROUP_RE =
note - do NOT match group phase or group playoff or such
for now only works for group a,b,c or group 1, group 2, etc. %r{\A Group [ ] (?: [a-z] | \d+ ) \z}ix
Instance Attribute Summary collapse
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
Class Method Summary collapse
- .debug=(value) ⇒ Object
-
.debug? ⇒ Boolean
note: default is FALSE.
Instance Method Summary collapse
-
#_build_date(m:, d:, y:, yy:, wday:, start:, last_year:) ⇒ Object
check - rename last_year to running_last_year to make intent clearer - why? why not?.
- #_is_group?(text) ⇒ Boolean
- #convert ⇒ Object
- #debug? ⇒ Boolean
- #errors? ⇒ Boolean
-
#initialize(tree, start: nil) ⇒ MatchTree
constructor
note: allow start(_date) nil if in use (start: nil) years expected on first date!!!.
- #log(msg) ⇒ Object
- #on_date_header(node) ⇒ Object
- #on_goal_line(node) ⇒ Object
- #on_group_def(node) ⇒ Object
- #on_match_line(node) ⇒ Object
- #on_match_line_bye(node) ⇒ Object
- #on_match_line_walkover(node) ⇒ Object
- #on_round_def(node) ⇒ Object
- #on_round_outline(node) ⇒ Object
Constructor Details
#initialize(tree, start: nil) ⇒ MatchTree
note: allow start(_date) nil
if in use (start: nil) years expected on first date!!!
19 20 21 22 23 24 |
# File 'lib/sportdb/quick/match_tree.rb', line 19 def initialize( tree, start: nil ) @tree = tree @start = start @errors = [] end |
Instance Attribute Details
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
26 27 28 |
# File 'lib/sportdb/quick/match_tree.rb', line 26 def errors @errors end |
Class Method Details
.debug=(value) ⇒ Object
7 |
# File 'lib/sportdb/quick/match_tree.rb', line 7 def self.debug=(value) @@debug = value; end |
.debug? ⇒ Boolean
note: default is FALSE
8 |
# File 'lib/sportdb/quick/match_tree.rb', line 8 def self.debug?() @@debug ||= false; end |
Instance Method Details
#_build_date(m:, d:, y:, yy:, wday:, start:, last_year:) ⇒ Object
check - rename last_year to running_last_year to make intent clearer - why? why not?
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/sportdb/quick/match_tree-helpers.rb', line 16 def _build_date( m:, d:, y:, yy:, wday:, start:, last_year: ) if m.nil? || d.nil? puts "[debug] !! ERROR - _build_date required month or day missing:" pp [m,d,y,yy,wday,start] exit 1 end ## quick debug hack if m == 2 && d == 29 puts "quick check feb/29 dates" pp [d,m,y] pp start end #### ## support two digit shortcut for year if yy ### ## for now assume 00,01 to 30 is 2000,2001 to 2030 ## and 31 to 99 is 1931 to 1999 y = yy <= 30 ? 2000+yy : 1900+yy end if y.nil? ## try to calculate year if last_year && @last_year ## use new formula y = @last_year elsif start.nil? puts "!! ERROR - _build_date - year expected for (first) date; cannot infer/guess; sorry" exit 1 else ## fallback to "old" formula - FIX/FIX remove later ## puts "[deprecated] WARN - do NOT use old year (date) auto-complete; add year to first date" y = if m > start.month || (m == start.month && d >= start.day) # assume same year as start_at event (e.g. 2013 for 2013/14 season) start.year else # assume year+1 as start_at event (e.g. 2014 for 2013/14 season) start.year+1 end end else ### note - reset @start to new date ## use @last_year if last_year @last_year = y puts " [debug] _build_date - set running last_year to #{y}" end end date = Date.new( y,m,d ) ## y,m,d ### todo/fix ### check/validate wday here date end |
#_is_group?(text) ⇒ Boolean
96 97 98 99 |
# File 'lib/sportdb/quick/match_tree_on/on_round_outline.rb', line 96 def _is_group?( text ) ## use regex for match GROUP_RE.match?( text ) end |
#convert ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/sportdb/quick/match_tree.rb', line 30 def convert ## note: every (new) read call - resets errors list to empty @errors = [] @warns = [] ## track list of warnings (unmatched lines) too - why? why not? ### todo/fix - FIX/FIX ## check start year from first date ## for now (auto-)update - @start with every date that incl. a year!!! @last_year = nil @last_date = nil @last_time = nil ## todo/fix - use stack push/pop in the future - why? why not? @last_round = nil ## merge - "top-level" - Round struct @last_round_name1 = nil ## level 1 - string @last_round_name2 = nil ## level 2 - string @last_round_name3 = nil ## level 3 - string @last_group = nil @teams = Hash.new(0) ## track counts (only) for now for (interal) team stats - why? why not? @rounds = {} @groups = {} @matches = [] @tree.each do |node| if node.is_a? RaccMatchParser::RoundDef ## todo/fix: add round definition (w begin n end date) ## todo: do not patch rounds with definition (already assume begin/end date is good) ## -- how to deal with matches that get rescheduled/postponed? on_round_def( node ) elsif node.is_a? RaccMatchParser::GroupDef ## NB: group goes after round (round may contain group marker too) ### todo: add pipe (|) marker (required) on_group_def( node ) elsif node.is_a? RaccMatchParser::RoundOutline on_round_outline( node ) elsif node.is_a? RaccMatchParser::DateHeader on_date_header( node ) elsif node.is_a? RaccMatchParser::MatchLine on_match_line( node ) elsif node.is_a? RaccMatchParser::MatchLineWalkover on_match_line_walkover( node ) elsif node.is_a? RaccMatchParser::MatchLineBye on_match_line_bye( node ) elsif node.is_a? RaccMatchParser::GoalLine on_goal_line( node ) elsif node.is_a?( RaccMatchParser::LineupLine ) || node.is_a?( RaccMatchParser::RefereeLine ) ## skip lineup, referee props for now elsif node.is_a?( RaccMatchParser::Heading1 ) || node.is_a?( RaccMatchParser::Heading2 ) || node.is_a?( RaccMatchParser::Heading3 ) ### skip headings (1/2/3) for now elsif node.is_a?( RaccMatchParser::BlankLine ) ### skip for now; do nothing else ## report error msg = "!! WARN - unknown node (parse tree type) - #{node.class.name}" puts msg pp node log( msg ) log( node.pretty_inspect ) end end # tree.each ## note - team keys are names and values are "internal" stats e.g. usage count!! ## and NOT team/club/nat_team structs!! [@teams.keys, @matches, @rounds.values, @groups.values] end |
#debug? ⇒ Boolean
9 |
# File 'lib/sportdb/quick/match_tree.rb', line 9 def debug?() self.class.debug?; end |
#errors? ⇒ Boolean
27 |
# File 'lib/sportdb/quick/match_tree.rb', line 27 def errors?() @errors.size > 0; end |
#log(msg) ⇒ Object
5 6 7 8 9 10 11 12 |
# File 'lib/sportdb/quick/match_tree-helpers.rb', line 5 def log( msg ) ## append msg to ./logs.txt ## use ./errors.txt - why? why not? File.open( './logs.txt', 'a:utf-8' ) do |f| f.write( msg ) f.write( "\n" ) end end |
#on_date_header(node) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/sportdb/quick/match_tree_on/on_date_header.rb', line 7 def on_date_header( node ) logger.debug( "date header: >#{node}<") date = _build_date( m: node.date[:m], d: node.date[:d], y: node.date[:y], yy: node.date[:yy], wday: node.date[:wday], start: @start, last_year: true ) logger.debug( " date: #{date} with start: #{@start}") @last_date = date # keep a reference for later use @last_time = nil ### quick "corona" hack - support seasons going beyond 12 month (see swiss league 2019/20 and others!!) ## find a better way?? ## set @start date to full year (e.g. 1.1.) if date.year is @start.year+1 ## todo/fix: add to linter to check for chronological dates!! - warn if NOT chronological ### todo/check: just turn on for 2019/20 season or always? why? why not? ## todo/fix: add switch back to old @start_org ## if year is date.year == @start.year-1 -- possible when full date with year set!!! =begin if @start.month != 1 if date.year == @start.year+1 logger.debug( "!! hack - extending start date to full (next/end) year; assumes all dates are chronologigal - always moving forward" ) @start_org = @start ## keep a copy of the original (old) start date - why? why not? - not used for now @start = Date.new( @start.year+1, 1, 1 ) end end =end end |
#on_goal_line(node) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/sportdb/quick/match_tree_on/on_goal_line.rb', line 5 def on_goal_line( node ) logger.debug "on goal line: >#{node}<" goals1 = node.goals1 goals2 = node.goals2 pp [goals1,goals2] if debug? ## special rule ## if goals 2 empty check if score for team 1 is zero ## and team 2 is NOT zero than ## make goals1 goald2!! ## e.g. Norway 0-1 Austria ## (Hof 32) if goals2.empty? && !goals1.empty? match = @matches[-1] ## ## todo/fix ## move upstream ## use score1_zero? or such - why? why not? if (match.score.is_a?(Array) && match.score[0] == 0 ) || (match.score.is_a?(Hash) && match.score[:et] && match.score[:et][0] == 0) || (match.score.is_a?(Hash) && match.score[:et].nil? && match.score[:ft] && match.score[:ft][0] == 0) ## "parallel assignment (or multiple assignment") - swap values in single line goals2, goals1 = goals1, goals2 end end goals = [] goals1.each do |rec| rec.minutes.each do |minute| goal = Goal.new( player: rec.player, team: 1, minute: minute.m, offset: minute.offset, penalty: minute.pen || false, # note: pass along/use false NOT nil owngoal: minute.og || false ) goals << goal end end goals2.each do |rec| rec.minutes.each do |minute| goal = Goal.new( player: rec.player, team: 2, minute: minute.m, offset: minute.offset, penalty: minute.pen || false, # note: pass along/use false NOT nil owngoal: minute.og || false ) goals << goal end end pp goals if debug? ## quick & dirty - auto add goals to last match ## note - for hacky (quick& dirty) multi-line support ## always append for now match = @matches[-1] match.goals ||= [] match.goals += goals ## todo/fix ## sort by minute ## PLUS auto-fill score1,score2 - why? why not? end |
#on_group_def(node) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/sportdb/quick/match_tree_on/on_group_def.rb', line 5 def on_group_def( node ) logger.debug "on group def: >#{node}<" ## e.g ## [:group_def, "Group A"], ## [:team, "Germany"], ## [:team, "Scotland"], ## [:team, "Hungary"], ## [:team, "Switzerland"] node.teams.each do |team| @teams[ team ] += 1 end group = Group.new( name: node.name, teams: node.teams ) @groups[ node.name ] = group end |
#on_match_line(node) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/sportdb/quick/match_tree_on/on_match_line.rb', line 6 def on_match_line( node ) logger.debug( "on match: >#{node}<" ) ## collect (possible) nodes by type num = nil num = node.num if node.num date = nil date = _build_date( m: node.date[:m], d: node.date[:d], y: node.date[:y], yy: node.date[:yy], wday: node.date[:wday], start: @start, last_year: true ) if node.date ## note - there's no time (-only) type in ruby ## use string (e.g. '14:56', '1:44') ## use 01:44 or 1:44 ? ## check for 0:00 or 24:00 possible? time = nil if node.time time = ('%d:%02d' % [node.time[:h], node.time[:m]]) ## check for timezone time += " #{node.time[:timezone]}" if node.time[:timezone] end ## todo/fix - ## keep time & time_local as pairs for @last_time/@last_time_local ## - check for timezone ## incl. timezone in time (string) - why? why not? time_local = nil if node.time_local time_local = ('%d:%02d' % [node.time_local[:h], node.time_local[:m]]) time_local += " #{node.time_local[:timezone]}" if node.time_local[:timezone] end ### todo/fix ## add keywords (e.g. ht, ft or such) to Score.new - why? why not? ## or use new Score.build( ht:, ft:, ) or such - why? why not? ## pp score score = nil score = node.score if node.score ## if node.score.is_a?(Array) ## ## assume "undefined" score ## score = node.score ## else ## (default) assume Hash ## # ht = node.score[:ht] || [nil,nil] ## # ft = node.score[:ft] || [nil,nil] ## # et = node.score[:et] || [nil,nil] ## # p = node.score[:p] || [nil,nil] ## # values = [*ht, *ft, *et, *p] ## # pp values ## ## pp node.score ## score = node.score ## end ## end status = nil status = node.status if node.status ### assume text for now ## if node_type == :status # e.g. awarded, canceled, postponed, etc. ## status = node[1] # ## todo - add ## find (optional) match status e.g. [abandoned] or [replay] or [awarded] ## or [cancelled] or [postponed] etc. ## status = find_status!( line ) ## todo/check: allow match status also in geo part (e.g. after @) - why? why not? team1 = node.team1 team2 = node.team2 @teams[ team1 ] += 1 @teams[ team2 ] += 1 if node.header ## note - date/time for matches (w/ header) CANNOT get inherited!! @last_date = nil @last_time = nil else ## no (match header), use date/time inheritance rules ### # check if date found? # note: ruby falsey is nil & false only (not 0 or empty array etc.) if date ### check: use date_v2 if present? why? why not? @last_date = date # keep a reference for later use @last_time = nil # @last_time = nil else date = @last_date # no date found; (re)use last seen date end if time @last_time = time else time = @last_time end end group = nil group = @last_group if @last_group round = nil round = @last_round if @last_round ### try auto-fill round ## find (first) matching round by date if rounds / matchdays defined ## if not rounds / matchdays defined - YES, allow matches WITHOUT rounds!!! if date && round.nil? if @rounds.size > 0 @rounds.values.each do |round_rec| ## note: convert date to date only (no time) with to_date!!! if (round_rec.start_date && round_rec.end_date) && (date.to_date >= round_rec.start_date && date.to_date <= round_rec.end_date) round = round_rec break end end if round.nil? ## todo/fix - issue a warning (do NOT stop) puts "!! PARSE ERROR - no matching round found for match date:" pp date exit 1 end end end ## todo/check: scores are integers or strings? ## todo/check: pass along round and group refs or just string (canonical names) - why? why not? ## split date in date & time if DateTime =begin time_str = nil date_str = nil if date.is_a?( DateTime ) date_str = date.strftime('%Y-%m-%d') time_str = date.strftime('%H:%M') elsif date.is_a?( Date ) date_str = date.strftime('%Y-%m-%d') else # assume date is nil end =end time_str = nil time_local_str = nil date_str = nil date_str = date.strftime('%Y-%m-%d') if date time_str = time if date && time time_local_str = time_local if date && time_local ground = nil ground = node.geo if node.geo ## attendance att = nil att = node.att if node.att @matches << Match.new( num: num, date: date_str, time: time_str, time_local: time_local_str, team1: team1, ## note: for now always use mapping value e.g. rec (NOT string e.g. team1.name) team2: team2, ## note: for now always use mapping value e.g. rec (NOT string e.g. team2.name) score: score, round: round ? round.name : nil, ## note: for now always use string (assume unique canonical name for event) group: group ? group.name : nil, ## note: for now always use string (assume unique canonical name for event) status: status, ground: ground, att: att ) ### todo: cache team lookups in hash? end |
#on_match_line_bye(node) ⇒ Object
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/sportdb/quick/match_tree.rb', line 136 def on_match_line_bye( node ) logger.debug( "on match (bye): >#{node}<" ) ## note - bye records NO date/time or ground (or score etc.) ## for now only team1/team2 and match status!! ## plus inherited round/group status = 'bye' team = node.team @teams[ team ] += 1 group = nil group = @last_group if @last_group round = nil round = @last_round if @last_round @matches << Match.new( team1: team, ## note: for now always use mapping value e.g. rec (NOT string e.g. team1.name) round: round ? round.name : nil, ## note: for now always use string (assume unique canonical name for event) group: group ? group.name : nil, ## note: for now always use string (assume unique canonical name for event) status: status ) ### todo: cache team lookups in hash? end |
#on_match_line_walkover(node) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/sportdb/quick/match_tree.rb', line 106 def on_match_line_walkover( node ) logger.debug( "on match (w/o): >#{node}<" ) ## note - w/o (walkover) records NO date/time or ground (or score etc.) ## for now only team1/team2 and match status!! ## plus inherited round/group status = 'walkover' ## use w/o - why? why not? team1 = node.team1 team2 = node.team2 @teams[ team1 ] += 1 @teams[ team2 ] += 1 group = nil group = @last_group if @last_group round = nil round = @last_round if @last_round @matches << Match.new( team1: team1, ## note: for now always use mapping value e.g. rec (NOT string e.g. team1.name) team2: team2, ## note: for now always use mapping value e.g. rec (NOT string e.g. team2.name) round: round ? round.name : nil, ## note: for now always use string (assume unique canonical name for event) group: group ? group.name : nil, ## note: for now always use string (assume unique canonical name for event) status: status ) ### todo: cache team lookups in hash? end |
#on_round_def(node) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/sportdb/quick/match_tree_on/on_round_def.rb', line 5 def on_round_def( node ) logger.debug "on round def: >#{node}<" ## e.g. [[:round_def, "Matchday 1"], [:duration, "Fri Jun 14 - Tue Jun 18"]] ## [[:round_def, "Matchday 2"], [:duration, "Wed Jun 19 - Sat Jun 22"]] ## [[:round_def, "Matchday 3"], [:duration, "Sun Jun 23 - Wed Jun 26"]] name = node.name # NB: use extracted round name for knockout check # knockout_flag = is_knockout_round?( name ) ## ## note - do NOT update @last_year on round def dates!! ## only update for running dates in matches!! if node.date start_date = end_date = _build_date( m: node.date[:m], d: node.date[:d], y: node.date[:y], yy: node.date[:yy], wday: node.date[:wday], start: @start, last_year: false ) elsif node.duration ## reuse year in start date e.g. July 26-27 1930 ## July 26 [1930], [July] 27 1930 start_date = _build_date( m: node.duration[:start][:m], d: node.duration[:start][:d], y: node.duration[:start][:y] || node.duration[:end][:y], yy: node.duration[:start][:yy] || node.duration[:end][:yy], wday: node.duration[:start][:wday], start: @start, last_year: false ) ## reuse month in end date e.g. July 26-27 ## July 26, [July] 27 end_date = _build_date( m: node.duration[:end][:m] || node.duration[:start][:m], d: node.duration[:end][:d], y: node.duration[:end][:y], yy: node.duration[:end][:yy], wday: node.duration[:end][:wday], start: @start, last_year: false ) else puts "!! PARSE ERROR - expected date or duration for round def; got:" pp node exit 1 end # note: - NOT needed; start_at and end_at are saved as date only (NOT datetime) # set hours,minutes,secs to beginning and end of day (do NOT use default 12.00) # e.g. use 00.00 and 23.59 # start_at = start_at.beginning_of_day # end_at = end_at.end_of_day # note: make sure start_at/end_at is date only (e.g. use start_at.to_date) # sqlite3 saves datetime in date field as datetime, for example (will break date compares later!) # note - _build_date always returns Date for now - no longer needed!! # start_date = start_date.to_date # end_date = end_date.to_date logger.debug " start_date: #{start_date}" logger.debug " end_date: #{end_date}" logger.debug " name: >#{name}<" round = Round.new( name: name, start_date: start_date, end_date: end_date, auto: false ) @rounds[ name ] = round end |
#on_round_outline(node) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/sportdb/quick/match_tree_on/on_round_outline.rb', line 6 def on_round_outline( node ) logger.debug "on round outline: >#{node}<" ## always reset dates - why? why not? ## note - needs last_date for year ## track last_year with extra variable name = node.outline level = node.level #### # check for "old" group header in ("automagic") round outline for now ## ## todo/fix - use only names from group def for lookup/is_group match!!! ## do NOT use (generic) regex!! if level == 1 && _is_group?( name ) logger.debug "on group header: >#{node}<" group = @groups[ name ] if group # set group for matches @last_group = group # note: group header resets (last) round (allows, for example): # e.g. # Group Playoffs/Replays -- round header # team1 team2 -- match # Group B -- group header # team1 team2 - match (will get new auto-matchday! not last round) @last_round = nil return ## note - return here; do NOT fall through to std round processing! else puts "!! WARN - no group def found for >#{name}<; will use a (plain) round" end end ## is_group? ## ## todo/fix - also reset round name levels on heading 1/2/3 etc. ## why? why not? if level == 1 @last_round_name1 = name @last_round_name2 = nil @last_round_name3 = nil elsif level == 2 @last_round_name2 = name @last_round_name3 = nil name = [@last_round_name1, name].join( ', ' ) elsif level == 3 @last_round_name3 == name name = [@last_round_name1, @last_round_name2, name].join( ', ') else puts "!! ERROR - unsupported round outline level #{level}; use 1-3 - sorry" exit 1 end round = @rounds[ name ] if round.nil? ## auto-add / create if missing round = Round.new( name: name ) @rounds[ name ] = round end @last_round = round @last_group = nil # note: always reset group to no group - why? why not? ## todo/fix/check ## make round a scope for date(time) - why? why not? ## reset date/time e.g. @last_date = nil !!!! end |