diff --git a/app/models/concerns/team_analytics.rb b/app/models/concerns/team_analytics.rb new file mode 100644 index 00000000..ea9e0f96 --- /dev/null +++ b/app/models/concerns/team_analytics.rb @@ -0,0 +1,87 @@ +module TeamAnalytics + extend ActiveSupport::Concern + # TODO, Get out out redis + included do + SECTIONS = %w(team-details members about-members big-headline big-quote challenges favourite-benefits + organization-style office-images jobs stack protips why-work interview-steps + locations team-blog) + + def record_exit(viewer, exit_url, exit_target_type, furthest_scrolled, time_spent) + epoch_now = Time.now.to_i + user_id = (viewer.respond_to?(:id) && viewer.try(:id)) || viewer + data = visitor_data(exit_url, exit_target_type, furthest_scrolled, time_spent, user_id, epoch_now, nil) + Redis.current.zadd(user_detail_views_key, epoch_now, data) + end + + def detailed_visitors(since = 0) + Redis.current.zrangebyscore(user_detail_views_key, since, Time.now.to_i).map do |visitor_string| + visitor = some_crappy_method(visitor_string) + visitor[:user] = identify_visitor(visitor[:user_id]) + visitor + end + end + + def simple_visitors(since = 0) + all_visitors = Redis.current.zrangebyscore(user_views_key, since, Time.now.to_i, withscores: true) + + Redis.current.zrangebyscore(user_anon_views_key, since, Time.now.to_i, withscores: true) + Hash[*all_visitors.flatten].map do |viewer_id, timestamp| + visitor_data(nil, nil, nil, 0, viewer_id, timestamp, identify_visitor(viewer_id)) + end + end + + def visitors(since = 0) + detailed_visitors = self.detailed_visitors + first_detailed_visit = detailed_visitors.last.nil? ? updated_at : detailed_visitors.first[:visited_at] + self.detailed_visitors(since) + simple_visitors(since == 0 ? first_detailed_visit.to_i : since) + end + + def aggregate_visitors(since = 0) + aggregate = {} + visitors(since).map do |visitor| + user_id = visitor[:user_id].to_i + aggregate[user_id] ||= visitor + aggregate[user_id].merge!(visitor) do |key, old, new| + case key + when :time_spent + old.to_i + new.to_i + when :visited_at + [old.to_i, new.to_i].max + when :furthest_scrolled + SECTIONS[[SECTIONS.index(old) || 0, SECTIONS.index(new) || 0].max] + else + old.nil? ? new : old + end + end + aggregate[user_id][:visits] ||= 0 + aggregate[user_id][:visits] += 1 + + end + aggregate.values.sort { |a, b| b[:visited_at] <=> a[:visited_at] } + end + + def sections_up_to(furthest) + SECTIONS.slice(0, SECTIONS.index(furthest)) + end + + def number_of_completed_sections(*excluded_sections) + completed_sections = 0 + + sections = (SECTIONS - excluded_sections).map do |section| + "has_#{section.gsub(/-/, '_')}?" + end + sections.each do |section_complete| + completed_sections += 1 if self.respond_to?(section_complete) && + public_send(section_complete) + end + completed_sections + end + + private + def some_crappy_method(hash_string_to_parse) + # This code is bad and Mike should feel bad. + JSON.parse('{' + hash_string_to_parse.gsub(/^{|}$/, '').split(', ') + .map { |pair| pair.split('=>') } + .map { |k, v| [k.gsub(/^:(\w*)/, '"\1"'), v == 'nil' ? 'null' : v].join(': ') }.join(', ') + '}') + end + end +end diff --git a/app/models/team.rb b/app/models/team.rb index 72cc6e52..b6dead40 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -8,6 +8,7 @@ class Team include Tire::Model::Search include LeaderboardRedisRank include SearchModule + include TeamAnalytics # Disabled Team indexing because it slows down updates # we should BG this @@ -403,7 +404,7 @@ def has_featured_links? end def has_upcoming_events? - !upcoming_events.blank? + false end def has_team_blog? @@ -717,13 +718,6 @@ def fix_website_url! end end - #Will delete , it not even working - def upcoming_events - team_members.collect do |member| - - end - end - def active_jobs jobs[0...4] end @@ -741,59 +735,10 @@ def all_jobs Opportunity.where(team_document_id: self.id.to_s).order('created_at DESC') end - def record_exit(viewer, exit_url, exit_target_type, furthest_scrolled, time_spent) - epoch_now = Time.now.to_i - data = visitor_data(exit_url, exit_target_type, furthest_scrolled, time_spent, (viewer.respond_to?(:id) && viewer.try(:id)) || viewer, epoch_now, nil) - Redis.current.zadd(user_detail_views_key, epoch_now, data) - end - - def detailed_visitors(since = 0) - Redis.current.zrangebyscore(user_detail_views_key, since, Time.now.to_i).map do |visitor_string| - visitor = HashStringParser.better_than_eval(visitor_string) - visitor[:user] = identify_visitor(visitor[:user_id]) - visitor - end - end - - def simple_visitors(since = 0) - all_visitors = Redis.current.zrangebyscore(user_views_key, since, Time.now.to_i, withscores: true) + fRedis.current.zrangebyscore(user_anon_views_key, since, Time.now.to_i, withscores: true) - Hash[*all_visitors.flatten].collect do |viewer_id, timestamp| - visitor_data(nil, nil, nil, 0, viewer_id, timestamp, identify_visitor(viewer_id)) - end - end - def visitors(since=0) - detailed_visitors = self.detailed_visitors - first_detailed_visit = detailed_visitors.last.nil? ? self.updated_at : detailed_visitors.first[:visited_at] - self.detailed_visitors(since) + self.simple_visitors(since == 0 ? first_detailed_visit.to_i : since) - end - SECTIONS = %w(team-details members about-members big-headline big-quote challenges favourite-benefits organization-style office-images jobs stack protips why-work interview-steps locations team-blog) SECTION_FIELDS = %w(about headline big_quote our_challenge benefit_description_1 organization_way office_photos stack_list reason_name_1 interview_steps team_locations blog_feed) - def aggregate_visitors(since=0) - aggregate ={} - visitors(since).map do |visitor| - user_id = visitor[:user_id].to_i - aggregate[user_id] ||= visitor - aggregate[user_id].merge!(visitor) do |key, old, new| - case key - when :time_spent - old.to_i + new.to_i - when :visited_at - [old.to_i, new.to_i].max - when :furthest_scrolled - SECTIONS[[SECTIONS.index(old) || 0, SECTIONS.index(new) || 0].max] - else - old.nil? ? new : old - end - end - aggregate[user_id][:visits] ||= 0 - aggregate[user_id][:visits] += 1 - - end - aggregate.values.sort { |a, b| b[:visited_at] <=> a[:visited_at] } - end def visitors_interested_in_jobs aggregate_visitors.select { |visitor| visitor[:exit_target_type] == 'job-opportunity' }.collect { |visitor| visitor[:user_id] } @@ -807,10 +752,6 @@ def click_through_rate self.visitors_interested_in_jobs.count/self.total_views(self.upgraded_at) end - def sections_up_to(furthest) - SECTIONS.slice(0, SECTIONS.index(furthest)) - end - def coderwall? slug == 'coderwall' end @@ -846,15 +787,6 @@ def has_specified_enough_info? number_of_completed_sections >= 6 end - def number_of_completed_sections(*excluded_sections) - completed_sections = 0 - - (SECTIONS - excluded_sections).map { |section| "has_#{section.gsub(/-/, '_')}?" }.each do |section_complete| - completed_sections +=1 if self.respond_to?(section_complete) && self.send(section_complete) - end - completed_sections - end - def has_team_details? has_external_link? and !self.about.nil? and !self.avatar.nil? end diff --git a/lib/hash_string_parser.rb b/lib/hash_string_parser.rb deleted file mode 100644 index a45636ca..00000000 --- a/lib/hash_string_parser.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'json' - -class HashStringParser - def self.better_than_eval(hash_string_to_parse) - # This code is bad and I should feel bad. - JSON.parse("{" + hash_string_to_parse.gsub(/^{|}$/, '').split(', '). - map { |pair| pair.split('=>') }. - map {|k, v| [k.gsub(/^:(\w*)/, '"\1"'), v == 'nil' ? "null" : v].join(": ") }.join(", ") + "}") - end -end diff --git a/spec/lib/hash_string_parser_spec.rb b/spec/lib/hash_string_parser_spec.rb deleted file mode 100644 index 3ce92a30..00000000 --- a/spec/lib/hash_string_parser_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -# encoding: utf-8 - -RSpec.describe HashStringParser do - it 'converts a simple hash string to Ruby' do - expect(HashStringParser.better_than_eval('{:x=>"example"}')).to eq({'x' => 'example'}) - end - - it 'converts a simple hash string to Ruby with a nil' do - expect(HashStringParser.better_than_eval('{:x=>nil}')).to eq({'x' => nil}) - end - - it 'converts a simple hash string to Ruby with a number' do - expect(HashStringParser.better_than_eval('{:x=>1}')).to eq({'x' => 1}) - end - - it 'converts a simple hash string to Ruby with a null string' do - expect(HashStringParser.better_than_eval('{:x=>"null"}')).to eq({'x' => 'null'}) - end -end