def parse_fixtures( reader )

    reader.each_line do |line|

      if is_goals?( line )
        parse_goals( line )
      elsif is_round_def?( line )
        ## 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?
        parse_round_def( line )
      elsif is_round?( line )
        parse_round_header( line )
      elsif is_group_def?( line ) ## NB: group goes after round (round may contain group marker too)
        ### todo: add pipe (|) marker (required)
        parse_group_def( line )
      elsif is_group?( line )
        ##  -- lets you set group  e.g. Group A etc.
        parse_group_header( line )
      elsif try_parse_game( line )
        # do nothing here
      elsif try_parse_date_header( line )
        # do nothing here
      else
        logger.info "skipping line (no match found): >#{line}<"
      end
    end # lines.each

    ###########################
    # backtrack and patch round pos and round dates (start_at/end_at)
    #  note: patch dates must go first! (otherwise sort_by_date will not work for round pos)

    unless @patch_round_ids_dates.empty?
      ###
      #  fix: do NOT patch if auto flag is set to false !!!
      #   e.g. rounds got added w/ round def (not w/ round header)

      # note: use uniq - to allow multiple round headers (possible?)

      Round.find( @patch_round_ids_dates.uniq ).each do |r|
        logger.debug "patch round start_at/end_at date for #{r.title}:"

        ## note:
        ## will add "scope" pos first e.g
        #
        ## SELECT "games".* FROM "games"  WHERE "games"."round_id" = ?
        # ORDER BY pos, play_at asc  [["round_id", 7]]
        #   thus will NOT order by play_at but by pos first!!!
        # =>
        #  need to unscope pos!!! or use unordered_games - games_by_play_at_date etc.??
        #   thus use reorder()!!! - not just order('play_at asc')

        games = r.games.reorder( 'play_at asc' ).all

        ## skip rounds w/ no games

        ## todo/check/fix: what's the best way for checking assoc w/ 0 recs?
        next if games.size == 0

        # note: make sure start_at/end_at is date only (e.g. use play_at.to_date)
        #   sqlite3 saves datetime in date field as datetime, for example (will break date compares later!)

        round_attribs = {
          start_at: games[0].play_at.to_date,   # use games.first ?
          end_at:   games[-1].play_at.to_date  # use games.last ? why? why not?
        }

        logger.debug round_attribs.to_json
        r.update_attributes!( round_attribs )
      end
    end

    unless @patch_round_ids_pos.empty?

      # step 0: check for offset (last_round_pos)
      if @last_round_pos
        offset = @last_round_pos
        logger.info "  +++ patch round pos - use offset; start w/ #{offset}"
      else
        offset = 0
        logger.debug "  patch round pos - no offset; start w/ 0"
      end

      # step 1: sort by date
      # step 2: update pos
      # note: use uniq - to allow multiple round headers (possible?)
      Round.order( 'start_at asc').find( @patch_round_ids_pos.uniq ).each_with_index do |r,idx|
        # note: starts counting w/ zero(0)
        logger.debug "[#{idx+1}] patch round pos >#{offset+idx+1}< for #{r.title}:"
        round_attribs = {
          pos: offset+idx+1
        }

        # update title if Matchday XXXX  e.g. use Matchday 1 etc.
        if r.title.starts_with?('Matchday')
          round_attribs[:title] = "Matchday #{offset+idx+1}"
        end

        logger.debug round_attribs.to_json
        r.update_attributes!( round_attribs )

        # update last_round_pos offset too
        @last_round_pos = [offset+idx+1,@last_round_pos||0].max
      end
    end

  end
def parse_fixtures( reader )

    reader.each_line do |line|

      if is_goals?( line )
        parse_goals( line )
      elsif is_round_def?( line )
        ## 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?
        parse_round_def( line )
      elsif is_round?( line )
        parse_round_header( line )
      elsif is_group_def?( line ) ## NB: group goes after round (round may contain group marker too)
        ### todo: add pipe (|) marker (required)
        parse_group_def( line )
      elsif is_group?( line )
        ##  -- lets you set group  e.g. Group A etc.
        parse_group_header( line )
      elsif try_parse_game( line )
        # do nothing here
      elsif try_parse_date_header( line )
        # do nothing here
      else
        logger.info "skipping line (no match found): >#{line}<"
      end
    end # lines.each

    ###########################
    # backtrack and patch round pos and round dates (start_at/end_at)
    #  note: patch dates must go first! (otherwise sort_by_date will not work for round pos)

    unless @patch_round_ids_dates.empty?
      ###
      #  fix: do NOT patch if auto flag is set to false !!!
      #   e.g. rounds got added w/ round def (not w/ round header)

      # note: use uniq - to allow multiple round headers (possible?)

      Round.find( @patch_round_ids_dates.uniq ).each do |r|
        logger.debug "patch round start_at/end_at date for #{r.title}:"

        ## note:
        ## will add "scope" pos first e.g
        #
        ## SELECT "games".* FROM "games"  WHERE "games"."round_id" = ?
        # ORDER BY pos, play_at asc  [["round_id", 7]]
        #   thus will NOT order by play_at but by pos first!!!
        # =>
        #  need to unscope pos!!! or use unordered_games - games_by_play_at_date etc.??
        #   thus use reorder()!!! - not just order('play_at asc')

        games = r.games.reorder( 'play_at asc' ).all

        ## skip rounds w/ no games

        ## todo/check/fix: what's the best way for checking assoc w/ 0 recs?
        next if games.size == 0

        # note: make sure start_at/end_at is date only (e.g. use play_at.to_date)
        #   sqlite3 saves datetime in date field as datetime, for example (will break date compares later!)

        round_attribs = {
          start_at: games[0].play_at.to_date,   # use games.first ?
          end_at:   games[-1].play_at.to_date  # use games.last ? why? why not?
        }

        logger.debug round_attribs.to_json
        r.update_attributes!( round_attribs )
      end
    end

    unless @patch_round_ids_pos.empty?

      # step 0: check for offset (last_round_pos)
      if @last_round_pos
        offset = @last_round_pos
        logger.info "  +++ patch round pos - use offset; start w/ #{offset}"
      else
        offset = 0
        logger.debug "  patch round pos - no offset; start w/ 0"
      end

      # step 1: sort by date
      # step 2: update pos
      # note: use uniq - to allow multiple round headers (possible?)
      Round.order( 'start_at asc').find( @patch_round_ids_pos.uniq ).each_with_index do |r,idx|
        # note: starts counting w/ zero(0)
        logger.debug "[#{idx+1}] patch round pos >#{offset+idx+1}< for #{r.title}:"
        round_attribs = {
          pos: offset+idx+1
        }

        # update title if Matchday XXXX  e.g. use Matchday 1 etc.
        if r.title.starts_with?('Matchday')
          round_attribs[:title] = "Matchday #{offset+idx+1}"
        end

        logger.debug round_attribs.to_json
        r.update_attributes!( round_attribs )

        # update last_round_pos offset too
        @last_round_pos = [offset+idx+1,@last_round_pos||0].max
      end
    end

  end
method parse_goals
=begin
###### add to person and use!!!
def self.create_or_update_from_values( values, more_attribs={} )
    ## key & title required

    attribs, more_values = find_key_n_title( values )
    attribs = attribs.merge( more_attribs )

    ## check for optional values
    Person.create_or_update_from_attribs( attribs, more_values )
  end
=end