<commit_before># coding: UTF-8
require_relative './user/user_decorator'

class User < Sequel::Model
  include CartoDB::MiniSequel
  include CartoDB::UserDecorator

  one_to_one :client_application
  plugin :association_dependencies, :client_application => :destroy
  one_to_many :tokens, :class => :OauthToken
  one_to_many :maps
  one_to_many :assets
  one_to_many :data_imports
  one_to_many :geocodings, order: :created_at.desc

  many_to_many :layers, :order => :order, :after_add => proc { |user, layer|
    layer.set_default_order(user)
  }

  # Sequel setup & plugins
  set_allowed_columns :email, :map_enabled, :password_confirmation, 
    :quota_in_bytes, :table_quota, :account_type, :private_tables_enabled, 
    :period_end_date, :map_view_quota, :max_layers, :database_timeout, 
    :user_timeout, :map_view_block_price, :geocoding_quota, :dashboard_viewed_at,
    :sync_tables_enabled, :geocoding_block_price, :api_key, :notification
  plugin :validation_helpers
  plugin :json_serializer
  plugin :dirty


  # Restrict to_json attributes
  @json_serializer_opts = {
    :except => [ :crypted_password,
                 :salt,
                 :invite_token,
                 :invite_token_date,
                 :admin,
                 :enabled,
                 :map_enabled],
    :naked => true # avoid adding json_class to result
  }

  SYSTEM_TABLE_NAMES = %w( spatial_ref_sys geography_columns geometry_columns raster_columns raster_overviews cdb_tablemetadata )
  SCHEMAS = %w( public cdb_importer )

  self.raise_on_typecast_failure = false
  self.raise_on_save_failure = false

  ## Validations
  def validate
    super
    validates_presence :username
    validates_format /^[a-z0-9\-]+$/, :username, :message => "must only contain lowercase letters, numbers & hyphens"
    validates_presence :email
    validates_unique   :email, :message => 'is already taken'
    validates_format EmailAddressValidator::Regexp::ADDR_SPEC, :email, :message => 'is not a valid address'
    validates_presence :password if new? && (crypted_password.blank? || salt.blank?)

    if password.present? && ( password_confirmation.blank? || password != password_confirmation )
      errors.add(:password, "doesn't match confirmation")
    end
  end

  ## Callbacks
  def before_create
    super
    self.database_host ||= ::Rails::Sequel.configuration.environment_for(Rails.env)['host']
    self.api_key ||= self.class.make_token
  end

  def after_create
    super
    setup_user
    save_metadata
    monitor_user_notification
    sleep 3
    set_statement_timeouts
  end

  def after_save
    super
    save_metadata
    changes = (self.previous_changes.present? ? self.previous_changes.keys : [])
    set_statement_timeouts if changes.include?(:user_timeout) || changes.include?(:database_timeout)
  end

  def before_destroy
    # Remove user tables
    self.tables.all.each { |t| t.destroy }
    
    # Remove user data imports, maps and layers
    self.data_imports.each { |d| d.destroy }
    self.maps.each { |m| m.destroy }
    self.layers.each { |l| self.remove_layer l }

    # Remove metadata from redis
    $users_metadata.DEL(self.key)

    # Invalidate user cache
    self.invalidate_varnish_cache
  end

  def after_destroy_commit
    # Remove database
    Thread.new do
      conn = Rails::Sequel.connection
        conn.run("UPDATE pg_database SET datallowconn = 'false' WHERE datname = '#{database_name}'")
        conn.run("SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = '#{database_name}'")
        conn.run("DROP DATABASE #{database_name}")
        conn.run("DROP USER #{database_username}")
    end.join
    monitor_user_notification
  end

  def invalidate_varnish_cache
    CartoDB::Varnish.new.purge("obj.http.X-Cache-Channel ~ #{database_name}.*")
  end

  ## Authentication
  AUTH_DIGEST = '47f940ec20a0993b5e9e4310461cc8a6a7fb84e3'

  # allow extra vars for auth
  attr_reader :password
  attr_accessor :password_confirmation

  ##
  # SLOW! Checks map views for every user
  # delta: get users who are also this percentage below their limit.
  #        example: 0.20 will get all users at 80% of their map view limit
  #
  def self.overquota(delta = 0)
    User.where(enabled: true).all.select do |u|<commit_msg>CDB-1168 #resolve Fixes removing user database on postgresql >= 9.2<commit_after># coding: UTF-8
require_relative './user/user_decorator'

class User < Sequel::Model
  include CartoDB::MiniSequel
  include CartoDB::UserDecorator

  one_to_one :client_application
  plugin :association_dependencies, :client_application => :destroy
  one_to_many :tokens, :class => :OauthToken
  one_to_many :maps
  one_to_many :assets
  one_to_many :data_imports
  one_to_many :geocodings, order: :created_at.desc

  many_to_many :layers, :order => :order, :after_add => proc { |user, layer|
    layer.set_default_order(user)
  }

  # Sequel setup & plugins
  set_allowed_columns :email, :map_enabled, :password_confirmation, 
    :quota_in_bytes, :table_quota, :account_type, :private_tables_enabled, 
    :period_end_date, :map_view_quota, :max_layers, :database_timeout, 
    :user_timeout, :map_view_block_price, :geocoding_quota, :dashboard_viewed_at,
    :sync_tables_enabled, :geocoding_block_price, :api_key, :notification
  plugin :validation_helpers
  plugin :json_serializer
  plugin :dirty


  # Restrict to_json attributes
  @json_serializer_opts = {
    :except => [ :crypted_password,
                 :salt,
                 :invite_token,
                 :invite_token_date,
                 :admin,
                 :enabled,
                 :map_enabled],
    :naked => true # avoid adding json_class to result
  }

  SYSTEM_TABLE_NAMES = %w( spatial_ref_sys geography_columns geometry_columns raster_columns raster_overviews cdb_tablemetadata )
  SCHEMAS = %w( public cdb_importer )

  self.raise_on_typecast_failure = false
  self.raise_on_save_failure = false

  ## Validations
  def validate
    super
    validates_presence :username
    validates_format /^[a-z0-9\-]+$/, :username, :message => "must only contain lowercase letters, numbers & hyphens"
    validates_presence :email
    validates_unique   :email, :message => 'is already taken'
    validates_format EmailAddressValidator::Regexp::ADDR_SPEC, :email, :message => 'is not a valid address'
    validates_presence :password if new? && (crypted_password.blank? || salt.blank?)

    if password.present? && ( password_confirmation.blank? || password != password_<PASSWORD> )
      errors.add(:password, "does<PASSWORD>")
    end
  end

  ## Callbacks
  def before_create
    super
    self.database_host ||= ::Rails::Sequel.configuration.environment_for(Rails.env)['host']
    self.api_key ||= self.class.make_token
  end

  def after_create
    super
    setup_user
    save_metadata
    monitor_user_notification
    sleep 3
    set_statement_timeouts
  end

  def after_save
    super
    save_metadata
    changes = (self.previous_changes.present? ? self.previous_changes.keys : [])
    set_statement_timeouts if changes.include?(:user_timeout) || changes.include?(:database_timeout)
  end

  def before_destroy
    # Remove user tables
    self.tables.all.each { |t| t.destroy }
    
    # Remove user data imports, maps and layers
    self.data_imports.each { |d| d.destroy }
    self.maps.each { |m| m.destroy }
    self.layers.each { |l| self.remove_layer l }

    # Remove metadata from redis
    $users_metadata.DEL(self.key)

    # Invalidate user cache
    self.invalidate_varnish_cache
  end

  def after_destroy_commit
    # Remove database
    Thread.new do
      conn = Rails::Sequel.connection
        conn.run("UPDATE pg_database SET datallowconn = 'false' WHERE datname = '#{database_name}'")
        begin
          conn.run("SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = '#{database_name}'")
        rescue Sequel::DatabaseError => e
          conn.run("SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '#{database_name}'")
        end
        conn.run("DROP DATABASE #{database_name}")
        conn.run("DROP USER #{database_username}")
    end.join
    monitor_user_notification
  end

  def invalidate_varnish_cache
    CartoDB::Varnish.new.purge("obj.http.X-Cache-Channel ~ #{database_name}.*")
  end

  ## Authentication
  AUTH_DIGEST = '47f940ec20a0993b5e9e4310461cc8a6a7fb84e3'

  # allow extra vars for auth
  attr_reader :password
  attr_accessor :password_confirmation

  ##
  # SLOW! Checks map views for every user
  # delta: get users who are also this percentage below their limit.
  #        example: 0.20 will get all users at 80% of their map view limit
  #
  def self.overquota(delta = 0)
    User.where(enabled: true).all.select do |u|
{'fraction_non_alphanumeric': 0.09502623773671001, 'fraction_numerical': 0.00889801505817933, 'mean_word_length': 3.6608187134502925, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5171942', 'n_tokens_mistral': 2896, 'n_tokens_neox': 2791, 'n_words': 635}