diff --git a/db/manifest b/db/manifest index 97db4f4f6..bad9decf1 100755 --- a/db/manifest +++ b/db/manifest @@ -133,4 +133,7 @@ plays_refactor.sql fix_max_mind_isp_and_geo.sql update_get_work_for_client_type.sql events.sql +cascading_delete_constraints_for_release.sql +events_social_description.sql +fix_broken_cities.sql diff --git a/db/up/cascading_delete_constraints_for_release.sql b/db/up/cascading_delete_constraints_for_release.sql new file mode 100644 index 000000000..1b9e541fb --- /dev/null +++ b/db/up/cascading_delete_constraints_for_release.sql @@ -0,0 +1,18 @@ +-- allow a user to be readily deleted by adding cascades +ALTER TABLE music_sessions_user_history DROP CONSTRAINT music_sessions_user_history_user_id_fkey; +ALTER TABLE ONLY music_sessions_user_history ADD CONSTRAINT music_sessions_user_history_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; + +ALTER TABLE crash_dumps DROP CONSTRAINT crash_dumps_user_id_fkey; +ALTER TABLE ONLY crash_dumps ADD CONSTRAINT crash_dumps_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL; + +ALTER TABLE music_sessions_history DROP CONSTRAINT music_sessions_history_user_id_fkey; +ALTER TABLE music_sessions_history ADD CONSTRAINT music_sessions_history_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; + +ALTER TABLE music_sessions_user_history DROP CONSTRAINT "music_sessions_user_history_music_session_id_fkey"; +ALTER TABLE music_sessions_user_history ADD CONSTRAINT "music_sessions_user_history_music_session_id_fkey" FOREIGN KEY (music_session_id) REFERENCES music_sessions_history(music_session_id) ON DELETE CASCADE; + +ALTER TABLE "recordings" DROP CONSTRAINT "recordings_creator_id_fkey"; +ALTER TABLE "recordings" ADD CONSTRAINT "recordings_creator_id_fkey" FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE; + +ALTER TABLE playable_plays DROP CONSTRAINT "playable_plays_claimed_recording_id_fkey"; +ALTER TABLE playable_plays ADD CONSTRAINT "playable_plays_claimed_recording_id_fkey" FOREIGN KEY (claimed_recording_id) REFERENCES claimed_recordings(id) ON DELETE CASCADE; \ No newline at end of file diff --git a/db/up/events_social_description.sql b/db/up/events_social_description.sql new file mode 100644 index 000000000..36f3675e1 --- /dev/null +++ b/db/up/events_social_description.sql @@ -0,0 +1 @@ +ALTER TABLE events ADD COLUMN social_description TEXT; diff --git a/db/up/fix_broken_cities.sql b/db/up/fix_broken_cities.sql new file mode 100644 index 000000000..baffc7cf1 --- /dev/null +++ b/db/up/fix_broken_cities.sql @@ -0,0 +1,2 @@ +alter table cities drop column regionname; +alter table cities drop column countryname; diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 0353c58a0..b6103cd16 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -128,6 +128,9 @@ require "jam_ruby/models/geo_ip_locations" require "jam_ruby/models/score" require "jam_ruby/models/get_work" require "jam_ruby/models/playable_play" +require "jam_ruby/models/country" +require "jam_ruby/models/region" +require "jam_ruby/models/city" include Jampb diff --git a/ruby/lib/jam_ruby/models/city.rb b/ruby/lib/jam_ruby/models/city.rb new file mode 100644 index 000000000..282f56a7d --- /dev/null +++ b/ruby/lib/jam_ruby/models/city.rb @@ -0,0 +1,10 @@ +module JamRuby + class City < ActiveRecord::Base + + self.table_name = 'cities' + + def self.get_all(country, region) + self.where(countrycode: country).where(region: region).order('city asc').all + end + end +end diff --git a/ruby/lib/jam_ruby/models/country.rb b/ruby/lib/jam_ruby/models/country.rb new file mode 100644 index 000000000..ca64b222c --- /dev/null +++ b/ruby/lib/jam_ruby/models/country.rb @@ -0,0 +1,53 @@ +module JamRuby + class Country < ActiveRecord::Base + + self.table_name = 'countries' + + def self.get_all() + self.order('countryname asc').all + end + + def self.import_from_iso3166(file) + + # File iso3166.csv + # Format: + # countrycode,countryname + + # what this does is not replace the contents of the table, but rather update the specified rows with the names. + # any rows not specified have the countryname reset to be the same as the countrycode. + + self.transaction do + self.connection.execute "update #{self.table_name} set countryname = countrycode" + + File.open(file, 'r:ISO-8859-1') do |io| + saved_level = ActiveRecord::Base.logger ? ActiveRecord::Base.logger.level : 0 + count = 0 + + ncols = 2 + + csv = ::CSV.new(io, {encoding: 'ISO-8859-1', headers: false}) + csv.each do |row| + raise "file does not have expected number of columns (#{ncols}): #{row.length}" unless row.length == ncols + + countrycode = row[0] + countryname = row[1] + + stmt = "UPDATE #{self.table_name} SET countryname = #{MaxMindIsp.quote_value(countryname)} WHERE countrycode = #{MaxMindIsp.quote_value(countrycode)}" + self.connection.execute stmt + count += 1 + + if ActiveRecord::Base.logger and ActiveRecord::Base.logger.level < Logger::INFO + ActiveRecord::Base.logger.debug "... logging updates to #{self.table_name} suspended ..." + ActiveRecord::Base.logger.level = Logger::INFO + end + end + + if ActiveRecord::Base.logger + ActiveRecord::Base.logger.level = saved_level + ActiveRecord::Base.logger.debug "updated #{count} records in #{self.table_name}" + end + end # file + end # transaction + end + end +end diff --git a/ruby/lib/jam_ruby/models/event.rb b/ruby/lib/jam_ruby/models/event.rb index c21e655ab..5d041485e 100644 --- a/ruby/lib/jam_ruby/models/event.rb +++ b/ruby/lib/jam_ruby/models/event.rb @@ -1,9 +1,15 @@ class JamRuby::Event < ActiveRecord::Base - attr_accessible :slug, :title, :description, :show_sponser, as: :admin + attr_accessible :slug, :title, :description, :show_sponser, :social_description, as: :admin validates :slug, uniqueness: true, presence: true validates :show_sponser, :inclusion => {:in => [true, false]} + before_validation :sanitize_active_admin + + def sanitize_active_admin + self.social_description = nil if self.social_description == '' + end + has_many :event_sessions, class_name: 'JamRuby::EventSession' end diff --git a/ruby/lib/jam_ruby/models/geo_ip_locations.rb b/ruby/lib/jam_ruby/models/geo_ip_locations.rb index 0201ff679..e5ea8a653 100644 --- a/ruby/lib/jam_ruby/models/geo_ip_locations.rb +++ b/ruby/lib/jam_ruby/models/geo_ip_locations.rb @@ -132,7 +132,7 @@ module JamRuby ActiveRecord::Base.logger.debug "DELETE FROM #{REGIONS_TABLE} returned sts #{sts.cmd_status}" if ActiveRecord::Base.logger sts.check - sts = self.connection.execute "INSERT INTO #{REGIONS_TABLE} (region, countrycode) SELECT DISTINCT region, countrycode FROM #{CITIES_TABLE};" + sts = self.connection.execute "INSERT INTO #{REGIONS_TABLE} (region, regionname, countrycode) SELECT DISTINCT region, region, countrycode FROM #{CITIES_TABLE};" ActiveRecord::Base.logger.debug "INSERT INTO #{REGIONS_TABLE} returned sts #{sts.cmd_status}" if ActiveRecord::Base.logger sts.check @@ -140,7 +140,7 @@ module JamRuby ActiveRecord::Base.logger.debug "DELETE FROM #{COUNTRIES_TABLE} returned sts #{sts.cmd_status}" if ActiveRecord::Base.logger sts.check - sts = self.connection.execute "INSERT INTO #{COUNTRIES_TABLE} (countrycode) SELECT DISTINCT countrycode FROM #{REGIONS_TABLE};" + sts = self.connection.execute "INSERT INTO #{COUNTRIES_TABLE} (countrycode, countryname) SELECT DISTINCT countrycode, countrycode FROM #{REGIONS_TABLE};" ActiveRecord::Base.logger.debug "INSERT INTO #{COUNTRIES_TABLE} returned sts #{sts.cmd_status}" if ActiveRecord::Base.logger sts.check end diff --git a/ruby/lib/jam_ruby/models/region.rb b/ruby/lib/jam_ruby/models/region.rb new file mode 100644 index 000000000..decc05b9a --- /dev/null +++ b/ruby/lib/jam_ruby/models/region.rb @@ -0,0 +1,56 @@ +module JamRuby + class Region < ActiveRecord::Base + + self.table_name = 'regions' + + def self.get_all(country) + self.where(countrycode: country).order('regionname asc').all + end + + def self.import_from_xx_region(countrycode, file) + + # File xx_region.csv + # Format: + # region,regionname + + # what this does is not replace the contents of the table, but rather update the specifies rows with the names. + # any rows not specified are left alone. the parameter countrycode denote the country of the region (when uppercased) + + raise "countrycode (#{MaxMindIsp.quote_value(countrycode)}) is missing or invalid (it must be two characters)" unless countrycode and countrycode.length == 2 + countrycode = countrycode.upcase + + self.transaction do + self.connection.execute "update #{self.table_name} set regionname = region where countrycode = #{MaxMindIsp.quote_value(countrycode)}" + + File.open(file, 'r:ISO-8859-1') do |io| + saved_level = ActiveRecord::Base.logger ? ActiveRecord::Base.logger.level : 0 + count = 0 + + ncols = 2 + + csv = ::CSV.new(io, {encoding: 'ISO-8859-1', headers: false}) + csv.each do |row| + raise "file does not have expected number of columns (#{ncols}): #{row.length}" unless row.length == ncols + + region = row[0] + regionname = row[1] + + stmt = "UPDATE #{self.table_name} SET regionname = #{MaxMindIsp.quote_value(regionname)} WHERE countrycode = #{MaxMindIsp.quote_value(countrycode)} AND region = #{MaxMindIsp.quote_value(region)}" + self.connection.execute stmt + count += 1 + + if ActiveRecord::Base.logger and ActiveRecord::Base.logger.level < Logger::INFO + ActiveRecord::Base.logger.debug "... logging updates to #{self.table_name} suspended ..." + ActiveRecord::Base.logger.level = Logger::INFO + end + end + + if ActiveRecord::Base.logger + ActiveRecord::Base.logger.level = saved_level + ActiveRecord::Base.logger.debug "updated #{count} records in #{self.table_name}" + end + end # file + end # transaction + end + end +end diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index 92443499a..c11a7d6b8 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -153,7 +153,7 @@ context.JK.bindHoverEvents = function ($parent) { - if($parent) { + if(!$parent) { $parent = $('body'); } diff --git a/web/app/views/events/_event_session.html.haml b/web/app/views/events/_event_session.html.haml index 484ff1928..3bb2a449f 100644 --- a/web/app/views/events/_event_session.html.haml +++ b/web/app/views/events/_event_session.html.haml @@ -1,5 +1,5 @@ %hr{ class:'w60' } -.landing-band.event +.landing-band.event{'data-event-session' => event_session.id} = event_session_img(event_session) %br %br diff --git a/web/app/views/events/event.html.haml b/web/app/views/events/event.html.haml index ae260005d..866dcbdc5 100644 --- a/web/app/views/events/event.html.haml +++ b/web/app/views/events/event.html.haml @@ -1,5 +1,20 @@ - provide(:title, @event.title) + +-content_for :social_meta do + %meta{property: 'fb_app_id', content: Rails.application.config.facebook_app_id} + %meta{property: 'og:title', content: 'JamKazam'} + %meta{property: 'og:url', content: request.original_url} + %meta{property: 'og:description', content: @event.social_description || "Play music together over the Internet as if in the same room."} + %meta{property: 'og:image', content: request.protocol + request.host_with_port + image_path("web/logo-256.png")} + %meta{property: 'og:image:width', content: '256'} + %meta{property: 'og:image:height', content: '256'} + %meta{property: 'og:type', content: 'website'} + %meta{property: 'twitter:card', content: 'summary'} + %meta{property: 'twitter:site', content: '@jamkazam'} + %meta{property: 'twitter:title', content: 'JamKazam'} + %meta{property: 'twitter:description',content: @event.social_description || "Play music together over the Internet as if in the same room."} + .landing-content %h1= @event.title %p.w60= raw(@event.description) @@ -8,7 +23,7 @@ %h2 ARTIST LINEUP %br - = render :partial => "event_session", :collection => @event.event_sessions + = render :partial => "event_session", :collection => @event.event_sessions.order('starts_at') %br{clear:'all'} diff --git a/web/app/views/recordings/show.html.erb b/web/app/views/recordings/show.html.erb index 4e79ace09..970cc0ad4 100644 --- a/web/app/views/recordings/show.html.erb +++ b/web/app/views/recordings/show.html.erb @@ -115,6 +115,7 @@ $(function () { var showRecording = new JK.ShowRecording(JK.app); showRecording.initialize("<%= @claimed_recording.id %>", "<%= @claimed_recording.recording_id %>"); + }); <% end %> diff --git a/web/app/views/users/_feed_music_session_ajax.html.haml b/web/app/views/users/_feed_music_session_ajax.html.haml index bbdbb514c..31d0241d7 100644 --- a/web/app/views/users/_feed_music_session_ajax.html.haml +++ b/web/app/views/users/_feed_music_session_ajax.html.haml @@ -22,7 +22,7 @@ = image_tag 'content/icon_playbutton.png', width:20, height:20, class:'play-icon' = "{% if(data.feed_item['has_mount?']) { %}" %audio{preload: 'none'} - %source{src: '{{data.feed_item.music_session.mount.url}}', type: '{{data.feed_item.music_session.mime_type}}'} + %source{src: '{{data.feed_item.music_session.mount.url}}', type: '{{data.feed_item.music_session.mount.mime_type}}'} = '{% } %}' %span.session-status = '{{data.feed_item.helpers.status}}' diff --git a/web/lib/max_mind_manager.rb b/web/lib/max_mind_manager.rb index 20f1b7da6..4ed9c8c08 100644 --- a/web/lib/max_mind_manager.rb +++ b/web/lib/max_mind_manager.rb @@ -12,25 +12,28 @@ class MaxMindManager < BaseManager city = state = country = nil unless ip_address.nil? || ip_address !~ /^\d+\.\d+\.\d+\.\d+$/ - ActiveRecord::Base.connection_pool.with_connection do |connection| - pg_conn = connection.instance_variable_get("@connection") - ip_as_int = ip_address_to_int(ip_address) - pg_conn.exec("SELECT country, region, city FROM max_mind_geo WHERE ip_start <= $1 AND $2 <= ip_end limit 1", [ip_as_int, ip_as_int]) do |result| - if !result.nil? && result.ntuples > 0 - country = result[0]['country'] - state = result[0]['region'] - city = result[0]['city'] - end - end + #ActiveRecord::Base.connection_pool.with_connection do |connection| + # pg_conn = connection.instance_variable_get("@connection") + # ip_as_int = ip_address_to_int(ip_address) + # pg_conn.exec("SELECT country, region, city FROM max_mind_geo WHERE ip_start <= $1 AND $2 <= ip_end limit 1", [ip_as_int, ip_as_int]) do |result| + # if !result.nil? && result.ntuples > 0 + # country = result[0]['country'] + # state = result[0]['region'] + # city = result[0]['city'] + # end + # end + #end + ip_as_int = ip_address_to_int(ip_address) + block = GeoIpBlocks.lookup(ip_as_int) + location = block ? GeoIpLocations.lookup(block.locid) : nil + if location + country = location.countrycode + state = location.region + city = location.city end end - { - :city => city, - :state => state, - :country => country - } - + a = {:city => city, :state => state, :country => country} end def self.lookup_isp(ip_address) @@ -53,32 +56,41 @@ class MaxMindManager < BaseManager end def self.countries() - ActiveRecord::Base.connection_pool.with_connection do |connection| - pg_conn = connection.instance_variable_get("@connection") - pg_conn.exec("SELECT DISTINCT country FROM max_mind_geo ORDER BY country ASC").map do |tuple| - tuple["country"] - end - end + #ActiveRecord::Base.connection_pool.with_connection do |connection| + # pg_conn = connection.instance_variable_get("@connection") + # pg_conn.exec("SELECT DISTINCT country FROM max_mind_geo ORDER BY country ASC").map do |tuple| + # tuple["country"] + # end + #end + + # returns ordered array of Country objects (countrycode, countryname) + Country.get_all.map { |c| c.countrycode } end def self.regions(country) - ActiveRecord::Base.connection_pool.with_connection do |connection| - pg_conn = connection.instance_variable_get("@connection") - pg_conn.exec("SELECT DISTINCT region FROM max_mind_geo WHERE country = $1 ORDER BY region ASC", [country]).map do |tuple| - tuple["region"] - end - end + #ActiveRecord::Base.connection_pool.with_connection do |connection| + # pg_conn = connection.instance_variable_get("@connection") + # pg_conn.exec("SELECT DISTINCT region FROM max_mind_geo WHERE country = $1 ORDER BY region ASC", [country]).map do |tuple| + # tuple["region"] + # end + #end + + # returns an ordered array of Region objects (region, regionname, countrycode) + Region.get_all(country).map { |r| r.region } end def self.cities(country, region) - ActiveRecord::Base.connection_pool.with_connection do |connection| - pg_conn = connection.instance_variable_get("@connection") - pg_conn.exec("SELECT DISTINCT city FROM max_mind_geo WHERE country = $1 AND region = $2 ORDER BY city ASC", [country, region]).map do |tuple| - tuple["city"] - end - end + #ActiveRecord::Base.connection_pool.with_connection do |connection| + # pg_conn = connection.instance_variable_get("@connection") + # pg_conn.exec("SELECT DISTINCT city FROM max_mind_geo WHERE country = $1 AND region = $2 ORDER BY city ASC", [country, region]).map do |tuple| + # tuple["city"] + # end + #end + + # returns an ordered array of City (city, region, countrycode) + City.get_all(country, region).map { |c| c.city } end @@ -116,7 +128,14 @@ class MaxMindManager < BaseManager ]).clear end + @pg_conn.exec "DELETE FROM cities" + @pg_conn.exec "INSERT INTO cities (city, region, countrycode) SELECT DISTINCT city, region, country FROM max_mind_geo" + @pg_conn.exec "DELETE FROM regions" + @pg_conn.exec "INSERT INTO regions (region, regionname, countrycode) select distinct region, region, countrycode from cities" + + @pg_conn.exec "DELETE FROM countries" + @pg_conn.exec "INSERT INTO countries (countrycode, countryname) SELECT DISTINCT countrycode, countrycode FROM regions" end private diff --git a/web/lib/tasks/import_max_mind.rake b/web/lib/tasks/import_max_mind.rake index d07b92248..526c73514 100644 --- a/web/lib/tasks/import_max_mind.rake +++ b/web/lib/tasks/import_max_mind.rake @@ -24,13 +24,25 @@ namespace :db do JamIsp.import_from_max_mind ENV['file'] end + desc "Import a iso3166 country database (countrycodes and names); run like this: rake db:import_countries file=/path/to/iso3166.csv" + task import_countries: :environment do + Country.import_from_iso3166 ENV['file'] + end + + desc "Import a region database (regioncode, regionname); run like this: rake db:import_regions countrycode=XX file=/path/to/xx_region.csv" + task import_regions: :environment do + Region.import_from_xx_region(ENV['countrycode'], ENV['file']) + end + desc "Help" task help: :environment do - puts "bundle exec rake db:import_maxmind_isp file=/path/to/GeoIPISP-142.csv" - puts "bundle exec rake db:import_maxmind_geo file=/path/to/GeoIPCity.csv" - puts "bundle exec rake db:import_geoip_blocks file=/path/to/GeoIPCity-134-Blocks.csv" - puts "bundle exec rake db:import_geoip_locations file=/path/to/GeoIPCity-134-Location.csv" - puts "bundle exec rake db:import_jam_isp file=/path/to/GeoIPISP.csv" + puts "bundle exec rake db:import_maxmind_isp file=/path/to/GeoIPISP-142.csv # geo-142" + puts "bundle exec rake db:import_maxmind_geo file=/path/to/GeoIPCity.csv # geo-139" + puts "bundle exec rake db:import_geoip_blocks file=/path/to/GeoIPCity-134-Blocks.csv # geo-134" + puts "bundle exec rake db:import_geoip_locations file=/path/to/GeoIPCity-134-Location.csv # geo-134" + puts "bundle exec rake db:import_jam_isp file=/path/to/GeoIPISP.csv # geo-124" + puts "bundle exec rake db:import_countries file=/path/to/iso3166.csv # db/geodata" + puts "bundle exec rake db:import_regions countrycode=XX file=/path/to/xx_region.csv # db/geodata, both of them" end desc "Create a fake set of maxmind data" diff --git a/web/spec/features/event_spec.rb b/web/spec/features/event_spec.rb index c18d90fd0..4ea9dab33 100644 --- a/web/spec/features/event_spec.rb +++ b/web/spec/features/event_spec.rb @@ -110,5 +110,20 @@ describe "Events", :js => true, :type => :feature, :capybara_feature => true, :s find('.sessions-page .landing-band', text: band.name) # indication of session landing page find(".recording-controls[data-music-session=\"#{music_session_history.id}\"]") + # test that it sorts correctly by putting this earlier event first + @event_session2 = FactoryGirl.create(:event_session, event: @event) + @event_session2.starts_at = 4.hours.ago + @event_session2.save! + visit "/events/so_latency" + expect(page).to have_css(".landing-band.event[data-event-session=\"#{@event_session2.id}\"]") + + # test that it sorts correctly by putting this later event second + @event_session2.starts_at = 4.hours.from_now + @event_session2.save! + visit "/events/so_latency" + expect(page).to have_css(".landing-band.event[data-event-session=\"#{@event_session.id}\"]") + + + end end diff --git a/web/spec/managers/maxmind_manager_spec.rb b/web/spec/managers/maxmind_manager_spec.rb index 90a0932c1..86fffa19d 100644 --- a/web/spec/managers/maxmind_manager_spec.rb +++ b/web/spec/managers/maxmind_manager_spec.rb @@ -20,7 +20,7 @@ describe MaxMindManager do regions = MaxMindManager.regions("US") regions.length.should == 4 regions.first.should == "AB" - regions.last.should == "DE" # Based on sort order this will be the top. + regions.last.should == "DE" end it "looks up cities successfully" do diff --git a/web/spec/managers/user_manager_spec.rb b/web/spec/managers/user_manager_spec.rb index 61ee5eb31..ba4d3a33d 100644 --- a/web/spec/managers/user_manager_spec.rb +++ b/web/spec/managers/user_manager_spec.rb @@ -31,9 +31,9 @@ describe UserManager do @user.last_name.should == "smith" @user.email.should == "userman1@jamkazam.com" @user.email_confirmed.should be_false - @user.city.should be_nil - @user.state.should be_nil - @user.country.should be_nil + @user.city.should == 'Boston' + @user.state.should == 'MA' + @user.country.should == 'US' @user.instruments.length.should == 1 @user.subscribe_email.should be_true @user.signup_token.should_not be_nil @@ -93,8 +93,8 @@ describe UserManager do signup_confirm_url: "http://localhost:3000/confirm" ) @user.errors.any?.should be_false - @user.city.should == 'City 127' - @user.state.should == 'DE' + @user.city.should == 'Boston' + @user.state.should == 'MA' @user.country.should == 'US' end