<commit_before>require 'active_support/concern'

module CompletedRun
  extend ActiveSupport::Concern

  included do
    def splits
      segments
    end

    def segments
      if @segments_cache.nil?
        @segments_cache = (dynamodb_segments || [])
      end
      return @segments_cache
    end

    def segments_with_history
      if @segments_with_history_cache.nil?
        @segments_with_history_cache = segments
        @segments_with_history_cache.each do |segment|
          segment.history = segment.dynamodb_history
        end
      end
      return @segments_with_history_cache
    end

    def shortest_segment
      collapsed_splits.min_by(&:duration)
    end

    def longest_segment
      collapsed_splits.max_by(&:duration)
    end

    def median_segment_duration
      sorted_segments = collapsed_splits.map(&:duration).sort
      len = sorted_segments.size
      (sorted_segments[(len - 1) / 2] + sorted_segments[len / 2]) / 2.0
    end

    def short?
      time < 20.minutes
    end

    def history
      dynamodb_history
    end

    def best_known?
      category && time == category.best_known_run.try(:time)
    end

    def pb?
      user && category && self == user.pb_for(category)
    end

    def time
      read_attribute(:time).to_f
    end

    def has_golds?
      splits.all? { |split| split.best }
    end

    def total_playtime
      time = 0
      return time unless program == "livesplit"
      segments_with_history.each do |segment|
        time += segment.history.sum { |h| h[:duration_seconds].nil? ? 0 : h[:duration_seconds] }
      end
      return time
    end
  end
end<commit_msg>Prevent .sum from returning nil on empty arrays (#284)<commit_after>require 'active_support/concern'

module CompletedRun
  extend ActiveSupport::Concern

  included do
    def splits
      segments
    end

    def segments
      if @segments_cache.nil?
        @segments_cache = (dynamodb_segments || [])
      end
      return @segments_cache
    end

    def segments_with_history
      if @segments_with_history_cache.nil?
        @segments_with_history_cache = segments
        @segments_with_history_cache.each do |segment|
          segment.history = segment.dynamodb_history
        end
      end
      return @segments_with_history_cache
    end

    def shortest_segment
      collapsed_splits.min_by(&:duration)
    end

    def longest_segment
      collapsed_splits.max_by(&:duration)
    end

    def median_segment_duration
      sorted_segments = collapsed_splits.map(&:duration).sort
      len = sorted_segments.size
      (sorted_segments[(len - 1) / 2] + sorted_segments[len / 2]) / 2.0
    end

    def short?
      time < 20.minutes
    end

    def history
      dynamodb_history
    end

    def best_known?
      category && time == category.best_known_run.try(:time)
    end

    def pb?
      user && category && self == user.pb_for(category)
    end

    def time
      read_attribute(:time).to_f
    end

    def has_golds?
      splits.all? { |split| split.best }
    end

    def total_playtime
      time = 0
      segments_with_history.each do |segment|
        next if segment.history.blank?
        segment.history.each do |h|
          time += h[:duration_seconds] unless h[:duration_seconds].blank?
        end
      end
      return time
    end
  end
end
{'fraction_non_alphanumeric': 0.07599879843796936, 'fraction_numerical': 0.006007810153199159, 'mean_word_length': 2.3738601823708207, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4423036', 'n_tokens_mistral': 1073, 'n_tokens_neox': 1027, 'n_words': 237}