Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -19,4 +19,4 @@

# Populate response rate for developers
developers = Developer.joins(:conversations).distinct
developers.each{ UpdateDeveloperResponseRateJob.perform_now(_1) }
developers.each { |d| UpdateDeveloperResponseRateJob.perform_now(d) }
@@ -163,11 +163,12 @@ Note that `heroku/ruby` might already be present if you've deployed before.
## Background jobs

1. `bundle exec rake sitemap:refresh` - daily at 12:00 AM UTC
2. `bundle exec rake open_startup:refresh_metrics` - daily at 12:00 AM UTC
3. `bundle exec rake locations:utc_offset` - daily at 5:00 AM UTC
4. `bundle exec rake developer_digest:daily` - daily at 2:00 PM UTC
5. `bundle exec rake developer_digest:weekly` - daily at 2:00 PM UTC
6. `bundle exec rake developers:notify_stale_profiles` - daily at 8:00 AM UTC
1. `bundle exec rake open_startup:refresh_metrics` - daily at 12:00 AM UTC
1. `bundle exec rake developers:calculate_search_score` - daily at 7:00 AM UTC
1. `bundle exec rake developers:notify_stale_profiles` - daily at 8:00 AM UTC
1. `bundle exec rake locations:utc_offset` - daily at 5:00 AM UTC
1. `bundle exec rake developer_digest:daily` - daily at 2:00 PM UTC
1. `bundle exec rake developer_digest:weekly` - daily at 2:00 PM UTC

## Sitemap hosting on S3

@@ -28,7 +28,6 @@ The "named" developers cover most states of each field on the profile. There are
* `invisible@example.com` - This business was marked as spam and is hidden.
* `suspended@example.com` - This account has been suspended and they cannot use the site.


### Admins

There is one admin account that is not associated with a developer nor a business.
@@ -7,4 +7,9 @@ namespace :developers do
notified_developers = stale_developers.profile_reminder_notifications
Rails.logger.info "Notifying #{notified_developers.count} developer(s) about their stale profile"
end

desc "Resave all developers to trigger updating of their search score"
task calculate_search_score: :environment do
Developer.visible.find_each(&:save)
end
end
@@ -24,7 +24,7 @@ class DevelopersTest < ActionDispatch::IntegrationTest
assert_description_contains "looking for their"
end

test "developers are sorted newest first" do
test "developers are sorted by newest first" do
create_developer(hero: "Oldest")
create_developer(hero: "Newest")

@@ -34,6 +34,16 @@ class DevelopersTest < ActionDispatch::IntegrationTest
assert response.body.index("Newest") < response.body.index("Oldest")
end

test "developers can be sorted by their search score" do
create_developer(hero: "Lower Score", search_score: 10)
create_developer(hero: "Higher Score", search_score: 20)

get developers_path(sort: :recommended)

assert_select "button.font-medium[value=recommended]"
assert response.body.index("Higher Score") < response.body.index("Lower Score")
end

test "subscribers can filter developers by time zone" do
create_developer(hero: "Pacific", location_attributes: {utc_offset: PACIFIC_UTC_OFFSET})
user = users(:subscribed_business)
@@ -119,10 +129,10 @@ class DevelopersTest < ActionDispatch::IntegrationTest
developers(:prospect).update!(search_status: :open)

with_pagy_default_items(1) do
get developers_path(sort: :availability)
get developers_path(sort: :newest)
assert_select "#developers h2", count: 1
assert_select "#mobile-filters h2", count: 1
assert_select "a[href=?]", "/developers?sort=availability&page=2"
assert_select "a[href=?]", "/developers?sort=newest&page=2"
end
end

@@ -0,0 +1,98 @@
require "test_helper"

module Developers
class SearchScoreTest < ActiveSupport::TestCase
setup do
@developer = developers(:one)
end

test "large boost for high response rate" do
@developer.response_rate = HasBadges::HIGH_RESPONSE_RATE_CUTTOFF
@developer.conversations_count = 1
assert_equal 20, @developer.score_for(:response_rate)
end

test "large demotion for low response rate" do
@developer.response_rate = HasBadges::LOW_RESPONSE_RATE_CUTTOFF
@developer.conversations_count = 1
assert_equal(-20, @developer.score_for(:response_rate))
end

test "no change when no conversations" do
@developer.response_rate = HasBadges::LOW_RESPONSE_RATE_CUTTOFF
@developer.conversations_count = 0
assert_equal 0, @developer.score_for(:response_rate)
end

test "large boost for a source contributor" do
@developer.source_contributor = true
assert_equal 20, @developer.score_for(:source_contributor?)
end

test "medium boost for a scheduling link" do
@developer.scheduling_link = "savvycal.com"
assert_equal 10, @developer.score_for(:scheduling_link?)
end

test "large boost for profile updated in last month" do
@developer.profile_updated_at = 3.weeks.ago
assert_equal 20, @developer.score_for(:profile_updated_at)
end

test "medium boost for profile updated 1-3 months ago" do
@developer.profile_updated_at = 2.months.ago
assert_equal 10, @developer.score_for(:profile_updated_at)
end

test "no boost for profile updated 3-6 months ago" do
@developer.profile_updated_at = 5.months.ago
assert_equal 0, @developer.score_for(:profile_updated_at)
end

test "medium demotion for profile updated more than 6 months ago" do
@developer.profile_updated_at = 7.months.ago
assert_equal(-10, @developer.score_for(:profile_updated_at))
end

test "extra large boost for profiles added in the last week" do
@developer.created_at = 6.days.ago
assert_equal 30, @developer.score_for(:recently_added?)
end

test "medium boost for bios with more than 500 characters" do
@developer.bio = "X" * 501
assert_equal 10, @developer.score_for(:bio)
end

test "large demotion for bios with fewer than 50 characters" do
@developer.bio = "X" * 49
assert_equal(-20, @developer.score_for(:bio))
end

test "normalizes scores to 100" do
@developer.bio = "X" * 501 # +10
@developer.scheduling_link = "savvycal.com" # +10
@developer.profile_updated_at = 4.months.ago # 0
@developer.created_at = 2.weeks.ago # 0

@developer.update_search_score

score = 18 # (10 + 10) / 110, rounded
assert_equal score, @developer.search_score
end

test "max normalized score of 100" do
@developer.response_rate = HasBadges::HIGH_RESPONSE_RATE_CUTTOFF
@developer.conversations_count = 1 # +20
@developer.source_contributor = true # +20
@developer.scheduling_link = "savvycal.com" # +10
@developer.profile_updated_at = 3.weeks.ago # +20
@developer.created_at = 6.days.ago # +30
@developer.bio = "X" * 501 # +20

@developer.update_search_score

assert_equal 100, @developer.search_score
end
end
end
@@ -3,10 +3,10 @@
class DeveloperQueryTest < ActiveSupport::TestCase
include DevelopersHelper

test "sort is :availability and defaults to :newest" do
assert_equal DeveloperQuery.new(sort: "availability").sort, :availability

test "sort defaults to :newest" do
assert_equal DeveloperQuery.new(sort: "newest").sort, :newest

assert_equal DeveloperQuery.new(sort: "recommended").sort, :recommended
assert_equal DeveloperQuery.new(sort: "bogus").sort, :newest
assert_equal DeveloperQuery.new(sort: "").sort, :newest
assert_equal DeveloperQuery.new.sort, :newest
@@ -199,7 +199,7 @@ class DeveloperQueryTest < ActiveSupport::TestCase
test "returns hash with filters" do
utc_offsets = [PACIFIC_UTC_OFFSET, EASTERN_UTC_OFFSET]
filters = {
sort: :availability,
sort: :newest,
utc_offsets:,
role_types: [:part_time_contract],
role_levels: [:junior],
@@ -1,52 +1,48 @@
require "application_system_test_case"

class FiltersTest < ApplicationSystemTestCase
test "developers availability sort click adds sort param" do
test "recommended sort click adds sort param" do
visit developers_path
find(:css, "#sort button[type='button']").click
find(:css, "#sort button[type='submit'][value='availability']").click

assert_current_path(/sort=availability/)
sort_by "recommended"
assert_current_path(/sort=recommended/)
end

test "developers newest sort click adds sort param" do
visit developers_path
click_sort_by_newset
sort_by "newest"
assert_current_path(/sort=newest/)
end

test "developers sort order is persisted with work preferences filters" do
visit developers_path
find(:css, "[name='include_not_interested']").set(true)
click_sort_by_newset
sort_by "newest"

assert_current_path(/sort=newest/)
assert_current_path(/include_not_interested=1/)
end

test "applying multiple filters to developers" do
visit developers_path
sort_by "recommended"

find(:css, "#sort button[type='button']").click
find(:css, "#sort button[type='submit'][value='availability']").click

assert_current_path(/sort=availability/)
assert_current_path(/sort=recommended/)

find(:css, "[name='include_not_interested']").set(true)
find(:css, "input[type=submit]").click

assert_current_path(/sort=availability/)
assert_current_path(/sort=recommended/)
assert_current_path(/include_not_interested=1/)

click_sort_by_newset
sort_by "newest"

assert_current_path(/sort=newest/)
assert_current_path(/include_not_interested=1/)
end

def click_sort_by_newset
def sort_by(sort)
toggle_sort_dropdown
find(:css, "#sort button[type='submit'][value='newest']").click
find(:css, "#sort button[type='submit'][value='#{sort}']").click
end

def toggle_sort_dropdown