<commit_before># A sequence of Levels
class Script < ActiveRecord::Base
  include Seeded
  has_many :levels, through: :script_levels
  has_many :script_levels, -> { order('chapter ASC') }, dependent: :destroy # all script levels, even those w/ stages, are ordered by chapter, see Script#add_script
  has_many :stages, -> { order('position ASC') }, dependent: :destroy
  belongs_to :wrapup_video, foreign_key: 'wrapup_video_id', class_name: 'Video'
  belongs_to :user
  validates :name, presence: true, uniqueness: { case_sensitive: false}

  # Hardcoded scriptID constants used throughout the code
  TWENTY_HOUR_ID = 1
  HOC_ID = 2 # this is the old (2013) hour of code
  EDIT_CODE_ID = 3
  TWENTY_FOURTEEN_LEVELS_ID = 4
  BUILDER_ID = 5
  FLAPPY_ID = 6
  JIGSAW_ID = 7

  MAX_DEFAULT_LEVEL_ID = 8

  # name of the new (2014) hour of code script (which is a levelbuilder script, so does not have a deterministic id)
  HOC_NAME = 'hourofcode'

  # other scripts identified by names not ids
  FROZEN_NAME = 'frozen'
  PLAYLAB_NAME = 'playlab'
  SPECIAL_NAME = 'special'

  def self.twenty_hour_script
    @@twenty_hour_script ||= Script.includes(script_levels: {level: [:game, :concepts] }).find(TWENTY_HOUR_ID)
  end

  def self.frozen_script
    @@frozen_script ||=  Script.includes([{script_levels: [{level: [:game, :concepts] }, :stage]}, :stages]).find_by_name(FROZEN_NAME)
  end

  def self.hoc_script
    @@hoc_script ||= Script.includes([{script_levels: [{level: [:game, :concepts] }, :stage]}, :stages]).find_by_name(HOC_NAME)
  end

  def cached?
    self.name == FROZEN_NAME || self.name == HOC_NAME || self.id == TWENTY_HOUR_ID
  end

  def starting_level
    raise "Script #{name} has no level to start at" if script_levels.empty?
    candidate_level = script_levels.first.or_next_progression_level
    raise "Script #{name} has no valid progression levels (non-unplugged) to start at" unless candidate_level
    candidate_level
  end

  def self.get_from_cache(id)
    case id
      when TWENTY_HOUR_ID then twenty_hour_script
      when FROZEN_NAME then frozen_script
      when HOC_NAME then hoc_script
    else
      # a bit of trickery so we support both ids which are numbers and
      # names which are strings that may contain numbers (eg. 2-3)
      find_by = (id.to_i.to_s == id.to_s) ? :id : :name
      Script.find_by(find_by => id).tap do |s|
        raise ActiveRecord::RecordNotFound.new("Couldn't find Script with id|name=#{id}") unless s
      end
    end
  end

  def to_param
    if self.twenty_hour? || self.id == HOC_ID
      super
    else
      name
    end
  end

  def script_levels_from_game(game_id)
    self.script_levels.includes(:script).joins(:level).where(levels: {game_id: game_id})
  end

  def multiple_games?
    # simplified check to see if we are in a script that has only one game (stage)
    stages.many? ||
      (stages.empty? && levels.first.game_id != levels.last.game_id)
  end

  def legacy_curriculum?
    default_script?
  end

  def twenty_hour?
    self.id == TWENTY_HOUR_ID
  end

  def hoc?
    # note that now multiple scripts can be an 'hour of code' script
    self.id == HOC_ID || self.name == HOC_NAME || self.name == FROZEN_NAME || self.flappy? || self.name == PLAYLAB_NAME || self.name == SPECIAL_NAME
  end

  def flappy?
    self.id == FLAPPY_ID
  end

  def default_script?
    self.id <= MAX_DEFAULT_LEVEL_ID
  end

  def find_script_level(level_id)
    self.script_levels.detect { |sl| sl.level_id == level_id }
  end

  def self.twenty_hour_script
    Script.find(TWENTY_HOUR_ID)
  end

  def self.builder_script
    Script.find(BUILDER_ID)
  end

  def get_script_level_by_id(script_level_id)
    script_level_id = script_level_id.to_i
    self.script_levels.select { |sl| sl.id == script_level_id }.first
  end
<commit_msg>modify cached?, remove duplicate twenty_hour_script method<commit_after># A sequence of Levels
class Script < ActiveRecord::Base
  include Seeded
  has_many :levels, through: :script_levels
  has_many :script_levels, -> { order('chapter ASC') }, dependent: :destroy # all script levels, even those w/ stages, are ordered by chapter, see Script#add_script
  has_many :stages, -> { order('position ASC') }, dependent: :destroy
  belongs_to :wrapup_video, foreign_key: 'wrapup_video_id', class_name: 'Video'
  belongs_to :user
  validates :name, presence: true, uniqueness: { case_sensitive: false}

  # Hardcoded scriptID constants used throughout the code
  TWENTY_HOUR_ID = 1
  HOC_ID = 2 # this is the old (2013) hour of code
  EDIT_CODE_ID = 3
  TWENTY_FOURTEEN_LEVELS_ID = 4
  BUILDER_ID = 5
  FLAPPY_ID = 6
  JIGSAW_ID = 7

  MAX_DEFAULT_LEVEL_ID = 8

  # name of the new (2014) hour of code script (which is a levelbuilder script, so does not have a deterministic id)
  HOC_NAME = 'hourofcode'

  # other scripts identified by names not ids
  FROZEN_NAME = 'frozen'
  PLAYLAB_NAME = 'playlab'
  SPECIAL_NAME = 'special'

  def self.twenty_hour_script
    @@twenty_hour_script ||= Script.includes(script_levels: {level: [:game, :concepts] }).find(TWENTY_HOUR_ID)
  end

  def self.frozen_script
    @@frozen_script ||= Script.includes([{script_levels: [{level: [:game, :concepts] }, :stage]}, :stages]).find_by_name(FROZEN_NAME)
  end

  def self.hoc_script
    @@hoc_script ||= Script.includes([{script_levels: [{level: [:game, :concepts] }, :stage]}, :stages]).find_by_name(HOC_NAME)
  end

  def cached?
    self.equal?(Script.twenty_hour_script) || self.equal?(Script.frozen_script) || self.equal?(Script.hoc_script)
  end

  def starting_level
    raise "Script #{name} has no level to start at" if script_levels.empty?
    candidate_level = script_levels.first.or_next_progression_level
    raise "Script #{name} has no valid progression levels (non-unplugged) to start at" unless candidate_level
    candidate_level
  end

  def self.get_from_cache(id)
    case id
      when TWENTY_HOUR_ID then twenty_hour_script
      when FROZEN_NAME then frozen_script
      when HOC_NAME then hoc_script
    else
      # a bit of trickery so we support both ids which are numbers and
      # names which are strings that may contain numbers (eg. 2-3)
      find_by = (id.to_i.to_s == id.to_s) ? :id : :name
      Script.find_by(find_by => id).tap do |s|
        raise ActiveRecord::RecordNotFound.new("Couldn't find Script with id|name=#{id}") unless s
      end
    end
  end

  def to_param
    if self.twenty_hour? || self.id == HOC_ID
      super
    else
      name
    end
  end

  def script_levels_from_game(game_id)
    self.script_levels.includes(:script).joins(:level).where(levels: {game_id: game_id})
  end

  def multiple_games?
    # simplified check to see if we are in a script that has only one game (stage)
    stages.many? ||
      (stages.empty? && levels.first.game_id != levels.last.game_id)
  end

  def legacy_curriculum?
    default_script?
  end

  def twenty_hour?
    self.id == TWENTY_HOUR_ID
  end

  def hoc?
    # note that now multiple scripts can be an 'hour of code' script
    self.id == HOC_ID || self.name == HOC_NAME || self.name == FROZEN_NAME || self.flappy? || self.name == PLAYLAB_NAME || self.name == SPECIAL_NAME
  end

  def flappy?
    self.id == FLAPPY_ID
  end

  def default_script?
    self.id <= MAX_DEFAULT_LEVEL_ID
  end

  def find_script_level(level_id)
    self.script_levels.detect { |sl| sl.level_id == level_id }
  end

  def self.builder_script
    Script.find(BUILDER_ID)
  end

  def get_script_level_by_id(script_level_id)
    script_level_id = script_level_id.to_i
    self.script_levels.select { |sl| sl.id == script_level_id }.first
  end

{'fraction_non_alphanumeric': 0.09831756046267087, 'fraction_numerical': 0.00473186119873817, 'mean_word_length': 3.705627705627706, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, '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': '4307526', 'n_tokens_mistral': 2777, 'n_tokens_neox': 2624, 'n_words': 718}