From f146adf261c70b035afcf2e38ae6d7de8d0af489 Mon Sep 17 00:00:00 2001 From: Ahmed Khaled Date: Thu, 4 Jun 2020 22:20:56 +0200 Subject: [PATCH 1/2] initial recommendations --- src/app/helpers/recommendations_helper.rb | 17 +++++++++++++++++ src/app/views/functions/show.html.erb | 13 +++++++++++-- .../views/recommendations/_function.html.erb | 1 + .../recommendations/_recommendation.html.erb | 6 ++++++ .../recommendations/_recommendations.html.erb | 2 ++ 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/app/helpers/recommendations_helper.rb create mode 100644 src/app/views/recommendations/_function.html.erb create mode 100644 src/app/views/recommendations/_recommendation.html.erb create mode 100644 src/app/views/recommendations/_recommendations.html.erb diff --git a/src/app/helpers/recommendations_helper.rb b/src/app/helpers/recommendations_helper.rb new file mode 100644 index 00000000..ca6f6810 --- /dev/null +++ b/src/app/helpers/recommendations_helper.rb @@ -0,0 +1,17 @@ +module RecommendationsHelper + MAX_USER_RECCOMMENDED_FUNCTIONS = 5 + + module RecommendationsPartialPath + def to_partial_path + 'recommendations/function' + end + end + + def more_from_user(user) + most_liked = user.functions.order(likes_count: :desc).first + most_commented = user.functions.order(comments_count: :desc).first + most_saved = user.functions.order(saves_count: :desc).first + + [most_liked, most_saved, most_commented].uniq.map { |obj| obj.extend(RecommendationsPartialPath) } + end +end diff --git a/src/app/views/functions/show.html.erb b/src/app/views/functions/show.html.erb index 50832a1e..10747c49 100644 --- a/src/app/views/functions/show.html.erb +++ b/src/app/views/functions/show.html.erb @@ -1,5 +1,14 @@ <%= content_for :title, sprintf("%s - by: %s", @function.name, @user.name) %> -<%= render 'tags/tags', tags: @function.tags %> -<%= render @function, full: true %> + +
+
+ <%= render 'recommendations/recommendations', function: @function %> +
+
+ <%= render 'tags/tags', tags: @function.tags %> + <%= render @function, full: true %> +
+
+ <%= render @function.comments %> <%= render 'comments/form', comment: @comment if can?(@comment, :create) %> diff --git a/src/app/views/recommendations/_function.html.erb b/src/app/views/recommendations/_function.html.erb new file mode 100644 index 00000000..8e7b7892 --- /dev/null +++ b/src/app/views/recommendations/_function.html.erb @@ -0,0 +1 @@ +<%= link_to "#{function.user.username}/#{function.name}", [function.user, function], class: 'panel-block' %> diff --git a/src/app/views/recommendations/_recommendation.html.erb b/src/app/views/recommendations/_recommendation.html.erb new file mode 100644 index 00000000..de7d704f --- /dev/null +++ b/src/app/views/recommendations/_recommendation.html.erb @@ -0,0 +1,6 @@ +
+

+ <%= recommendation_title %> +

+ <%= render functions %> +
\ No newline at end of file diff --git a/src/app/views/recommendations/_recommendations.html.erb b/src/app/views/recommendations/_recommendations.html.erb new file mode 100644 index 00000000..30c01a45 --- /dev/null +++ b/src/app/views/recommendations/_recommendations.html.erb @@ -0,0 +1,2 @@ +<%= render 'recommendations/recommendation', recommendation_title: 'Similar Functions', functions: more_from_user(function.user) %> +<%= render 'recommendations/recommendation', recommendation_title: "More from #{function.user.name}", functions: more_from_user(function.user) %> From 3edfb9ce06d18f3c2c2f405081a24519b5dc1d57 Mon Sep 17 00:00:00 2001 From: Ahmed Khaled Date: Sun, 7 Jun 2020 04:00:08 +0200 Subject: [PATCH 2/2] implemented similar functions --- src/app/helpers/recommendations_helper.rb | 61 +++++++++++++++++-- src/app/views/functions/show.html.erb | 11 ++-- .../recommendations/_recommendation.html.erb | 16 +++-- .../recommendations/_recommendations.html.erb | 4 +- .../helpers/recommendations_helper_spec.rb | 14 +++++ 5 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 src/spec/helpers/recommendations_helper_spec.rb diff --git a/src/app/helpers/recommendations_helper.rb b/src/app/helpers/recommendations_helper.rb index ca6f6810..7b456c29 100644 --- a/src/app/helpers/recommendations_helper.rb +++ b/src/app/helpers/recommendations_helper.rb @@ -1,5 +1,5 @@ module RecommendationsHelper - MAX_USER_RECCOMMENDED_FUNCTIONS = 5 + MAX_RECOMMENDED_FUNCTIONS = 5 module RecommendationsPartialPath def to_partial_path @@ -7,11 +7,60 @@ def to_partial_path end end - def more_from_user(user) - most_liked = user.functions.order(likes_count: :desc).first - most_commented = user.functions.order(comments_count: :desc).first - most_saved = user.functions.order(saves_count: :desc).first + def more_from_user_function(function) + most_liked = user_most_function function.user, likes_count: :desc + most_commented = user_most_function function.user, comments_count: :desc + most_saved = user_most_function function.user, saves_count: :desc - [most_liked, most_saved, most_commented].uniq.map { |obj| obj.extend(RecommendationsPartialPath) } + recommended_functions = filter_unwanted [most_liked, most_commented, most_saved], function + override_partial_path recommended_functions, RecommendationsPartialPath + end + + def similar_functions(function) + popularity_query = generate_popularity_query + + popular_functions_from_tags = function.tags.map { |tag| tag.functions.order(popularity_query).first } + popular_functions = Function.order(Arel.sql(popularity_query)) + .limit(MAX_RECOMMENDED_FUNCTIONS - popular_functions_from_tags.count) + + recommended_functions = filter_unwanted [*popular_functions_from_tags, *popular_functions], function + override_partial_path recommended_functions, RecommendationsPartialPath + end + + private + + def override_partial_path(functions, partial_path_module) + functions.map { |obj| obj.extend(partial_path_module) } + end + + def generate_popularity_query + likes_wieght = 1 + comments_weight = 0.5 + saves_weight = 0.25 + + "( (likes_count * #{likes_wieght}) + (comments_count * #{comments_weight}) + (saves_count * #{saves_weight}) ) desc" + end + + # Returns the most x function for a certain user + # based on a criteria defined in `parameter`. + # + # @param user [User] + # @param parameter [Hash] + # @returns [Function] + def user_most_function(user, parameter) + user.functions.order(parameter).first + end + + # Filters an array of Functions. It removes an `unwated_function` + # and any `nil` variables in the `functions` array. + # + # @param functions [Array] + # + # @param unwanted_functon [Function] a function to be filtered + # out of the Functions array. + # + # @returns [Array] + def filter_unwanted(functions, unwanted_function) + functions.reject { |func| func.nil? || func == unwanted_function }.uniq end end diff --git a/src/app/views/functions/show.html.erb b/src/app/views/functions/show.html.erb index 10747c49..298f95cf 100644 --- a/src/app/views/functions/show.html.erb +++ b/src/app/views/functions/show.html.erb @@ -1,14 +1,13 @@ <%= content_for :title, sprintf("%s - by: %s", @function.name, @user.name) %>
-
- <%= render 'recommendations/recommendations', function: @function %> -
<%= render 'tags/tags', tags: @function.tags %> <%= render @function, full: true %> + <%= render @function.comments %> + <%= render 'comments/form', comment: @comment if can?(@comment, :create) %> +
+
+ <%= render 'recommendations/recommendations', function: @function %>
- -<%= render @function.comments %> -<%= render 'comments/form', comment: @comment if can?(@comment, :create) %> diff --git a/src/app/views/recommendations/_recommendation.html.erb b/src/app/views/recommendations/_recommendation.html.erb index de7d704f..b0055a7f 100644 --- a/src/app/views/recommendations/_recommendation.html.erb +++ b/src/app/views/recommendations/_recommendation.html.erb @@ -1,6 +1,10 @@ -
-

- <%= recommendation_title %> -

- <%= render functions %> -
\ No newline at end of file +<% unless functions.empty? %> + +
+

+ <%= recommendation_title %> +

+ <%= render functions %> +
+ +<% end %> diff --git a/src/app/views/recommendations/_recommendations.html.erb b/src/app/views/recommendations/_recommendations.html.erb index 30c01a45..2b9eac28 100644 --- a/src/app/views/recommendations/_recommendations.html.erb +++ b/src/app/views/recommendations/_recommendations.html.erb @@ -1,2 +1,2 @@ -<%= render 'recommendations/recommendation', recommendation_title: 'Similar Functions', functions: more_from_user(function.user) %> -<%= render 'recommendations/recommendation', recommendation_title: "More from #{function.user.name}", functions: more_from_user(function.user) %> +<%= render 'recommendations/recommendation', recommendation_title: 'Similar Functions', functions: similar_functions(function) %> +<%= render 'recommendations/recommendation', recommendation_title: "More from #{function.user.name}", functions: more_from_user_function(function) %> diff --git a/src/spec/helpers/recommendations_helper_spec.rb b/src/spec/helpers/recommendations_helper_spec.rb new file mode 100644 index 00000000..e79f1570 --- /dev/null +++ b/src/spec/helpers/recommendations_helper_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe RecommendationsHelper do + let!(:users) { create_list(:user, 5) } + let(:functions) { users.map { |user| create_list(:function, 5, user: user) }.flatten } + + describe '#more_from_user_function' do + it { expect(helper.more_from_user_function(functions.sample)).to include(a_kind_of(Function)) } + end + + describe '#similar_functions' do + it { expect(helper.similar_functions(functions.sample)).to include(a_kind_of(Function)) } + end +end