<commit_before>class Scenario < ActiveRecord::Base
  include Provider
  require 'open-uri'

  serialize :aws_prefixes
  # 'serialize' stores a native Ruby object such as a Hash or Array to the database
  #  as a string
  enum location: [:development, :production, :local, :custom, :test]

  # Associations
  # http://guides.rubyonrails.org/association_basics.html
  belongs_to :user
  has_many :clouds, dependent: :destroy
  has_many :questions, dependent: :destroy
  has_many :roles, dependent: :destroy
  has_many :recipes, dependent: :destroy
  has_many :groups, dependent: :destroy
  has_many :subnets, through: :clouds
  has_many :instances, through: :subnets
  has_one :statistic

  # Validations
  # http://guides.rubyonrails.org/active_record_validations.html
  validates_associated :clouds, :questions, :roles, :recipes, :groups, :user
  validates :user, presence: true
  validates :name, presence: true, format: { without: /\A_*_\z/ }
  validates :name, format: { with: /\A\w*\z/,
                             message: "can only contain alphanumeric and underscore" }
  validate :paths_exist, :validate_stopped

  # Custom validations methods
  # http://guides.rubyonrails.org/active_record_validations.html#custom-methods

  def paths_exist
    errors.add(:path, "#{path} does not exist") unless File.exists? path
    errors.add(:path, "#{path_yml} does not exist") unless File.exists? path_yml
    errors.add(:path, "#{path_recipes} does not exist") unless File.exists? path_recipes
  end

  def validate_stopped
    errors.add(:running, "can only modify scenario if it is stopped") unless stopped?
  end


  # Callbacks
  # http://guides.rubyonrails.org/active_record_callbacks.html
  after_create :get_aws_prefixes, :create_statistic
  before_destroy :validate_stopped, :save_questions_and_answers, prepend: true

  def get_aws_prefixes
    content = open('https://ip-ranges.amazonaws.com/ip-ranges.json').read
    arr = JSON.parse(content)["prefixes"]
            .select { |p| p["region"] == ENV["AWS_REGION"] }
            .map { |p| p["ip_prefix"] }
    self.update_attribute(:aws_prefixes, arr)
  end

  # Statistics

  def save_questions_and_answers
    if self.statistic
      self.statistic.save_questions_and_answers
      self.statistic.save_scenario_yml
    end
  end

  # File structure

  def destroy_dependents
    self.clouds.each do |cloud| cloud.destroy end
    self.groups.each do |group| group.destroy end
    self.roles.each do |role| role.destroy end
    self.recipes.each do |recipe| recipe.destroy end
    self.questions.each do |question| question.destroy end
  end

  def update_yml
    if not self.modifiable?
      self.errors.add(:customizable, "Scenario is not modifiable.")
      return false
    end
    if not self.modified?
      self.errors.add(:modified, "Scenario is not modified.")
      return false
    end

    yml = { 
      "Name" => self.name, 
      "Description" => self.description,
      "Instructions" => self.instructions,
      "InstructionsStudent" => self.instructions_student,
      "Groups" => nil,
      "Clouds" => nil,
      "Subnets" => nil,
      "Instances" => nil
    }

    yml["Roles"] = self.roles.empty? ? nil : self.roles.map { |r|
      { "Name"=>r.name, 
        "Packages" => r.packages.empty? ? nil : r.packages, 
        "Recipes"=>r.recipes.empty? ? nil : r.recipes.map { |rec| rec.name }
      }
    }

    yml["Groups"] = self.groups.empty? ? nil : self.groups.map { |group| 
      { "Name" => group.name,
        "Instructions" => group.instructions,
        "Access" => { 
          "Administrator" => group.instance_groups.none? {|ig| ig.administrator} ? nil : {
            "IP_Visible" => group.instance_groups.select{ |ig| ig.administrator and ig.ip_visible }.map{ |ig| ig.instance.name },
            "IP_Hidden" => group.instance_groups.select{ |ig| ig.administrator and not ig.ip_visible }.map{ |ig| ig.instance.name }
            },
          "User" => group.instance_groups.none? {|ig| not ig.administrator} ? nil : {
            "IP_Visible" => group.instance_groups.select{ |ig| not ig.administrator and ig.ip_visible }.map{ |ig| ig.instance.name },
            "IP_Hidden" => group.instance_groups.select{ |ig| not ig.administrator and not ig.ip_visible }.map{ |ig| ig.instance.name }
          }
        },
        "Users" => group.players.empty? ? nil : group.players.map { |p| { 
          "Login" => p.login, 
          "Password" => <PASSWORD>, 
          "Id" => self.has_student?(p.user) ? p.user_id : nil,
          "UserId" => p.user_id,
          "StudentGroupId" => p.student_group_id
          } 
        }
      }
    }

    yml["Clouds"] = self.clouds.empty? ? nil : self.clouds.map { |cloud|
      { 
      "Name" => cloud.name, 
      "CIDR_Block" => cloud.cidr_block,
      "Subnets" => cloud.subnets.empty? ? nil : cloud.subnets.map { |subnet| 
        {
        "Name" => subnet.name, <commit_msg>Update update_yml<commit_after>class Scenario < ActiveRecord::Base
  include Provider
  require 'open-uri'

  serialize :aws_prefixes
  # 'serialize' stores a native Ruby object such as a Hash or Array to the database
  #  as a string
  enum location: [:development, :production, :local, :custom, :test]

  # Associations
  # http://guides.rubyonrails.org/association_basics.html
  belongs_to :user
  has_many :clouds, dependent: :destroy
  has_many :questions, dependent: :destroy
  has_many :roles, dependent: :destroy
  has_many :recipes, dependent: :destroy
  has_many :groups, dependent: :destroy
  has_many :subnets, through: :clouds
  has_many :instances, through: :subnets
  has_one :statistic

  # Validations
  # http://guides.rubyonrails.org/active_record_validations.html
  validates_associated :clouds, :questions, :roles, :recipes, :groups, :user
  validates :user, presence: true
  validates :name, presence: true, format: { without: /\A_*_\z/ }
  validates :name, format: { with: /\A\w*\z/,
                             message: "can only contain alphanumeric and underscore" }
  validate :paths_exist, :validate_stopped

  # Custom validations methods
  # http://guides.rubyonrails.org/active_record_validations.html#custom-methods

  def paths_exist
    errors.add(:path, "#{path} does not exist") unless File.exists? path
    errors.add(:path, "#{path_yml} does not exist") unless File.exists? path_yml
    errors.add(:path, "#{path_recipes} does not exist") unless File.exists? path_recipes
  end

  def validate_stopped
    errors.add(:running, "can only modify scenario if it is stopped") unless stopped?
  end


  # Callbacks
  # http://guides.rubyonrails.org/active_record_callbacks.html
  after_create :get_aws_prefixes, :create_statistic
  before_destroy :validate_stopped, :save_questions_and_answers, prepend: true

  def get_aws_prefixes
    content = open('https://ip-ranges.amazonaws.com/ip-ranges.json').read
    arr = JSON.parse(content)["prefixes"]
            .select { |p| p["region"] == ENV["AWS_REGION"] }
            .map { |p| p["ip_prefix"] }
    self.update_attribute(:aws_prefixes, arr)
  end

  # Statistics

  def save_questions_and_answers
    if self.statistic
      self.statistic.save_questions_and_answers
      self.statistic.save_scenario_yml
    end
  end

  # File structure

  def destroy_dependents
    self.clouds.each do |cloud| cloud.destroy end
    self.groups.each do |group| group.destroy end
    self.roles.each do |role| role.destroy end
    self.recipes.each do |recipe| recipe.destroy end
    self.questions.each do |question| question.destroy end
  end

  def update_yml
    if not self.modifiable?
      self.errors.add(:customizable, "Scenario is not modifiable.")
      return false
    end
    if not self.modified?
      self.errors.add(:modified, "Scenario is not modified.")
      return false
    end

    yml = { 
      "Name" => self.name, 
      "Description" => self.description,
      "Instructions" => self.instructions,
      "InstructionsStudent" => self.instructions_student,
      "Groups" => nil,
      "Clouds" => nil,
      "Subnets" => nil,
      "Instances" => nil
    }

    yml["Roles"] = self.roles.empty? ? nil : self.roles.map { |r|
      { "Name"=>r.name, 
        "Packages" => r.packages.empty? ? nil : r.packages, 
        "Recipes"=>r.recipes.empty? ? nil : r.recipes.map { |rec| rec.name }
      }
    }

    yml["Groups"] = self.groups.empty? ? nil : self.groups.map { |group| 
      { "Name" => group.name,
        "Instructions" => group.instructions,
        "Access" => group.instance_groups.empty? ? nil : group.instance_groups.map { |access|
          { "Instance" => access.instance.name,
            "Administrator" => access.administrator,
            "IP_Visible" => access.ip_visible
          }
        },
        "Users" => group.players.empty? ? nil : group.players.map { |p| { 
          "Login" => p.login, 
          "Password" => <PASSWORD>, 
          "Id" => self.has_student?(p.user) ? p.user_id : nil,
          "UserId" => p.user_id,
          "StudentGroupId" => p.student_group_id
          } 
        }
      }
    }

    yml["Clouds"] = self.clouds.empty? ? nil : self.clouds.map { |cloud|
      { 
      "Name" => cloud.name, 
      "CIDR_Block" => cloud.cidr_block,
      "Subnets" => cloud.subnets.empty? ? nil : cloud.subnets.map { |subnet| 
        {
        "Name" => subnet.name, 
{'fraction_non_alphanumeric': 0.12963359760017143, 'fraction_numerical': 0.0, 'mean_word_length': 2.9107666527021365, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 62, 'https://': 2, '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': '6399893', 'n_tokens_mistral': 3139, 'n_tokens_neox': 3014, 'n_words': 686}