require_relative "../../app/model_factories/layer_factory" require_relative "../../app/model_factories/map_factory" require 'carto/mapcapped_visualization_updater' namespace :cartodb do namespace :vizs do desc "Purges broken visualizations due to bug during deletion." task :delete_inconsistent, [:username] => :environment do |t, args| username = args[:username] raise "You should pass a username param" unless username user = ::User[username: username] collection = Carto::Visualization.where(user_id: user.id) collection.each do |viz| if inconsistent?(viz) delete_with_confirmation(viz) end end end desc "Purges broken canonical visualizations without related tables" task :delete_inconsistent_canonical_viz_without_tables => :environment do |_| Carto::Visualization.joins("left join user_tables ut on visualizations.map_id = ut.map_id").where("visualizations.type = 'table' and ut.id is null").find_each do |viz| begin puts "Checking for deletion --> User: #{viz.user.username} | Viz id: #{viz.id}" if inconsistent_table?(viz) puts "Deleting viz --> User: #{viz.user.username} | Viz id: #{viz.id}" viz.destroy! end rescue StandardError => e puts "Error deleting viz #{viz.id}: #{e}" end end end desc "Create named maps for all eligible existing visualizations" task :create_named_maps, [:order] => :environment do |t, args| sort_order = args[:order] == ':desc' ? :desc : :asc puts "Retrieving by :created_at #{sort_order}" puts "> #{Time.now}" vqb = Carto::VisualizationQueryBuilder.new .with_types([ Carto::Visualization::TYPE_CANONICAL, Carto::Visualization::TYPE_DERIVED ]) .with_order(:created_at, sort_order) .build count = vqb.count current = 0 puts "Fetched ##{count} items" puts "> #{Time.now}" vqb.each do |vis| begin current += 1 Carto::NamedMaps::Api.new(vis).upsert if current % 50 == 0 print '.' end if current % 500 == 0 puts "\n> #{Time.now} #{current}/#{count}" end vis = nil rescue StandardError => ex printf "E" end end puts "\n> #{Time.now}\nFinished ##{count} items" end desc "Exports a .carto file including visualization metadata and the tables" task :export_full_visualization, [:vis_id] => :environment do |_, args| visualization_id = args[:vis_id] raise "Missing visualization id argument" unless visualization_id visualization = Carto::Visualization.where(id: visualization_id).first raise "Visualization not found" unless visualization file = Carto::VisualizationExport.new.export(visualization, visualization.user) puts "Visualization exported: #{file}" end desc "Updates visualizations auth tokens from named maps" task update_auth_tokens: :environment do |_| Carto::Visualization.find_each(conditions: "privacy = 'password'") do |visualization| puts "Updating #{visualization.id}" begin tokens = visualization.get_auth_tokens puts " from #{visualization.auth_token} to #{tokens.first}" visualization.update_column(:auth_token, tokens.first) rescue StandardError => e puts "ERROR #{e.inspect}" end end end desc "Have all Builder visualizations mapcapped. Dry mode: `bundle exec rake cartodb:vizs:mapcap_builder_visualizations['--dry']`" task :mapcap_builder_visualizations, [:dry] => :environment do |_, args| dry = args[:dry] == '--dry' puts "Mapcapping v3 visualizations. Dry mode #{dry ? 'on' : 'off'}" Carto::Visualization.find_each(conditions: "version = 3 and type = 'derived' and privacy != 'private'") do |visualization| begin if !visualization.mapcapped? puts "Mapcapping #{visualization.id}" Carto::Mapcap.create!(visualization_id: visualization.id) unless dry end rescue StandardError => e puts "ERROR mapcapping #{visualization}: #{e.inspect}" end end end desc "Create analyses for all v3 visualizations" task :create_analyses_for_v3_visualizations, [:dry] => :environment do |_, args| include Carto::MapcappedVisualizationUpdater dry = args[:dry] == '--dry' puts "Adding analyses to v3 visualizations. Dry mode #{dry ? 'on' : 'off'}" puts "=== STEP 1/2: Visualizations ===" v3_no_analyses = Carto::Visualization.joins('LEFT JOIN analyses ON visualizations.id = analyses.visualization_id') .where(version: 3, type: 'derived', analyses: { id: nil }) v3_no_analyses.find_each do |visualization| begin puts "Adding analyses to visualization #{visualization.id}" visualization.add_source_analyses unless dry rescue StandardError => e puts "ERROR adding analyses to visualization #{visualization.id}: #{e.inspect}" end end puts "=== STEP 2/2: Mapcaps ===" mapcap_no_analyses = Carto::Mapcap.where("json_array_length(export_json->'visualization'->'analyses') = 0") mapcap_no_analyses.find_each do |mapcap| puts "Adding analyses to mapcap #{mapcap.id}" unless dry begin rv = mapcap.regenerate_visualization rv.data_layers.each_with_index do |layer, index| analysis = Carto::Analysis.source_analysis_for_layer(layer, index) rv.analyses << analysis layer.options[:source] = analysis.natural_id layer.options[:letter] = analysis.natural_id.first end mapcap.export_json = export_in_memory_visualization(rv, rv.user) mapcap.save rescue StandardError => e puts "ERROR adding analyses to mapcap: #{mapcap.id}: #{e.inspect}" end end end end desc "Restore visualization from backup" task :restore_visualization, [:backup_id] => :environment do |_, args| include Carto::VisualizationBackupService backup_id = args[:backup_id] visualization = restore_visualization_backup(backup_id) puts "Error restoring visualization" if visualization.nil? puts "Visualization #{visualization.id} restored" unless visualization.nil? end private def inconsistent?(viz) (viz.table? && viz.related_tables.empty?) || (viz.derived? && viz.map.nil?) end def inconsistent_table?(viz) (viz.user_table.nil? && viz.related_tables.empty?) end def delete_with_confirmation(viz) display_info(viz) if ok_to_delete? viz.delete STDOUT.puts "deleted!" end end def display_info(viz) STDOUT.puts "\nviz.name = #{viz.name}" STDOUT.puts "viz.type = #{viz.type}" STDOUT.puts "viz.related_tables = #{viz.related_tables.map {|t| t.name}}" STDOUT.puts "viz.map_id = #{viz.map_id}" end def ok_to_delete? STDOUT.puts "About to delete. Are you sure? (y/n)" input = STDIN.gets.strip return input == 'y' end end end