<commit_before># encoding: utf-8
require_relative '../models/visualization/collection'
require_relative '../models/table/user_table'
require_relative '../helpers/bounding_box_helper'

class Map < Sequel::Model
  self.raise_on_save_failure = false

  one_to_many :tables, class: ::UserTable
  many_to_one :user

  many_to_many :layers,
               order: :order,
               after_add: proc { |map, layer| layer.set_default_order(map) }

  many_to_many :base_layers,
               clone: :layers,
               right_key: :layer_id

  many_to_many :data_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: { kind: "carto" }

  many_to_many :user_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: "kind in ('tiled', 'background', 'gmapsbase', 'wms')"

  many_to_many :carto_and_torque_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: "kind in ('carto', 'torque')"

  many_to_many :torque_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: { kind: "torque" }

  many_to_many :other_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: "kind not in ('carto', 'tiled', 'background', 'gmapsbase', 'wms')"

  many_to_many :named_maps_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: "kind in ('tiled', 'background', 'gmapsbase', 'wms', 'carto')"

  plugin :association_dependencies, layers: :nullify

  PUBLIC_ATTRIBUTES = %w{ id user_id provider bounding_box_sw
                          bounding_box_ne center zoom view_bounds_sw view_bounds_ne legends scrollwheel }

  # FE code, so (lat,lon)
  DEFAULT_OPTIONS = {
    zoom:            3,
    bounding_box_sw: [BoundingBoxHelper::DEFAULT_BOUNDS[:minlat], BoundingBoxHelper::DEFAULT_BOUNDS[:minlon]],
    bounding_box_ne: [BoundingBoxHelper::DEFAULT_BOUNDS[:maxlat], BoundingBoxHelper::DEFAULT_BOUNDS[:maxlon]],
    provider:        'leaflet',
    center:          [30, 0]
  }

  MAXIMUM_ZOOM = 18

  attr_accessor :table_id,
                # Flag to detect if being destroyed by whom so invalidate_vizjson_varnish_cache skips it
                :being_destroyed_by_vis_id

  def before_save
    super
    self.updated_at = Time.now
  end

  def after_save
    super
    update_map_on_associated_entities
    notify_map_change
  end

  def notify_map_change
    visualization = visualizations.first

    force_notify_map_change unless visualization && visualization.latest_mapcap
  end

  def force_notify_map_change
    update_related_named_maps
    invalidate_vizjson_varnish_cache
  end

  def before_destroy
    raise CartoDB::InvalidMember.new(user: "Viewer users can't destroy maps") if user && user.viewer
    super
    invalidate_vizjson_varnish_cache
  end

  def public_values
    Hash[PUBLIC_ATTRIBUTES.map { |a| [a, send(a)] }]
  end

  def validate
    super
    errors.add(:user_id, "can't be blank") if user_id.blank?
    errors.add(:user, "Viewer users can't save maps") if user && user.viewer
  end

  def recalculate_bounds!
    set_boundaries(get_map_bounds)
    save
  rescue Sequel::DatabaseError => exception
    CartoDB::notify_exception(exception, user: user)
  end

  def set_default_boundaries!
    bounds = get_map_bounds
    set_boundaries(bounds)
    recenter_using_bounds(bounds)
    recalculate_zoom(bounds)
    save
  rescue Sequel::DatabaseError => exception
    CartoDB::notify_exception(exception, user: user)
  end

  def viz_updated_at
    latest_mapcap = visualizations.first.latest_mapcap

    latest_mapcap ? latest_mapcap.created_at : get_the_last_time_tiles_have_changed_to_render_it_in_vizjsons
  end

  def invalidate_vizjson_varnish_cache
    visualizations.each do |visualization|
      visualization.invalidate_cache unless visualization.id == being_destroyed_by_vis_id
    end
  end

  def update_related_named_maps
    visualizations.each do |visualization|
      visualization.save_named_map unless visualization.id == being_destroyed_by_vis_id
    end
  end

  def admits_layer?(layer)
    return admits_more_torque_layers? if layer.torque_layer?
    return admits_more_data_layers? if layer.data_layer?
    return admits_more_base_layers?(layer) if layer.base_layer?
  end

  def can_add_layer(user)
    return false if self.user.max_layers && self.user.max_layers <= carto_and_torque_layers.count
    return false if self.user.viewer

    current_vis = visualizations.first
    current_vis.has_permission?(user, CartoDB::Visualization::Member::PERMISSION_READWRITE)
  end

  def visualizations
    @visualizations_collection ||= CartoDB::Visualization::Collection.new.fetch(map_id: [id]).to_a
  end

  def process_privacy_in(layer)
    return self unless layer.uses_private_tables?

    visualizations.each do |visualization|
      unless visualization.organization?
        visualization.privacy = 'private'
        visualization.store<commit_msg>define constant for max layers<commit_after># encoding: utf-8
require_relative '../models/visualization/collection'
require_relative '../models/table/user_table'
require_relative '../helpers/bounding_box_helper'

class Map < Sequel::Model
  self.raise_on_save_failure = false

  one_to_many :tables, class: ::UserTable
  many_to_one :user

  many_to_many :layers,
               order: :order,
               after_add: proc { |map, layer| layer.set_default_order(map) }

  many_to_many :base_layers,
               clone: :layers,
               right_key: :layer_id

  many_to_many :data_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: { kind: "carto" }

  many_to_many :user_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: "kind in ('tiled', 'background', 'gmapsbase', 'wms')"

  many_to_many :carto_and_torque_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: "kind in ('carto', 'torque')"

  many_to_many :torque_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: { kind: "torque" }

  many_to_many :other_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: "kind not in ('carto', 'tiled', 'background', 'gmapsbase', 'wms')"

  many_to_many :named_maps_layers,
               clone: :layers,
               right_key: :layer_id,
               conditions: "kind in ('tiled', 'background', 'gmapsbase', 'wms', 'carto')"

  plugin :association_dependencies, layers: :nullify

  PUBLIC_ATTRIBUTES = %w{ id user_id provider bounding_box_sw
                          bounding_box_ne center zoom view_bounds_sw view_bounds_ne legends scrollwheel }

  # FE code, so (lat,lon)
  DEFAULT_OPTIONS = {
    zoom:            3,
    bounding_box_sw: [BoundingBoxHelper::DEFAULT_BOUNDS[:minlat], BoundingBoxHelper::DEFAULT_BOUNDS[:minlon]],
    bounding_box_ne: [BoundingBoxHelper::DEFAULT_BOUNDS[:maxlat], BoundingBoxHelper::DEFAULT_BOUNDS[:maxlon]],
    provider:        'leaflet',
    center:          [30, 0]
  }

  MAXIMUM_ZOOM = 18
  MAX_LAYERS = 8

  attr_accessor :table_id,
                # Flag to detect if being destroyed by whom so invalidate_vizjson_varnish_cache skips it
                :being_destroyed_by_vis_id

  def before_save
    super
    self.updated_at = Time.now
  end

  def after_save
    super
    update_map_on_associated_entities
    notify_map_change
  end

  def notify_map_change
    visualization = visualizations.first

    force_notify_map_change unless visualization && visualization.latest_mapcap
  end

  def force_notify_map_change
    update_related_named_maps
    invalidate_vizjson_varnish_cache
  end

  def before_destroy
    raise CartoDB::InvalidMember.new(user: "Viewer users can't destroy maps") if user && user.viewer
    super
    invalidate_vizjson_varnish_cache
  end

  def public_values
    Hash[PUBLIC_ATTRIBUTES.map { |a| [a, send(a)] }]
  end

  def validate
    super
    errors.add(:user_id, "can't be blank") if user_id.blank?
    errors.add(:user, "Viewer users can't save maps") if user && user.viewer
  end

  def recalculate_bounds!
    set_boundaries(get_map_bounds)
    save
  rescue Sequel::DatabaseError => exception
    CartoDB::notify_exception(exception, user: user)
  end

  def set_default_boundaries!
    bounds = get_map_bounds
    set_boundaries(bounds)
    recenter_using_bounds(bounds)
    recalculate_zoom(bounds)
    save
  rescue Sequel::DatabaseError => exception
    CartoDB::notify_exception(exception, user: user)
  end

  def viz_updated_at
    latest_mapcap = visualizations.first.latest_mapcap

    latest_mapcap ? latest_mapcap.created_at : get_the_last_time_tiles_have_changed_to_render_it_in_vizjsons
  end

  def invalidate_vizjson_varnish_cache
    visualizations.each do |visualization|
      visualization.invalidate_cache unless visualization.id == being_destroyed_by_vis_id
    end
  end

  def update_related_named_maps
    visualizations.each do |visualization|
      visualization.save_named_map unless visualization.id == being_destroyed_by_vis_id
    end
  end

  def admits_layer?(layer)
    return admits_more_torque_layers? if layer.torque_layer?
    return admits_more_data_layers? if layer.data_layer?
    return admits_more_base_layers?(layer) if layer.base_layer?
  end

  def can_add_layer(user)
    return false if carto_and_torque_layers.count > MAX_LAYERS
    return false if self.user.viewer

    current_vis = visualizations.first
    current_vis.has_permission?(user, CartoDB::Visualization::Member::PERMISSION_READWRITE)
  end

  def visualizations
    @visualizations_collection ||= CartoDB::Visualization::Collection.new.fetch(map_id: [id]).to_a
  end

  def process_privacy_in(layer)
    return self unless layer.uses_private_tables?

    visualizations.each do |visualization|
      unless visualization.organization?
        visualization.privacy = 'private'
        visualization.store
{'fraction_non_alphanumeric': 0.08168097070139095, 'fraction_numerical': 0.001479727730097662, 'mean_word_length': 3.234753550543024, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 8, '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': '718329', 'n_tokens_mistral': 3295, 'n_tokens_neox': 3161, 'n_words': 670}