Commit 2fd621c3d7332efcdb65b6e7045269ada32ad58c
Committed by
Tallys Martins
1 parent
d841eedf
Exists in
master
and in
5 other branches
Add software statistic block
Signed-off-by: brenddongontijo <brenddongontijo@msn.com> Signed-off-by: Fabio Teixeira <fabio1079@gmail.com> Signed-off-by: Pedro de Lyra <pedrodelyra@gmail.com> Signed-off-by: Luciano Prestes Cavalcanti <lucianopcbr@gmail.com> Signed-off-by: Simiao Carvalho <simiaosimis@gmail.com> Signed-off-by: Victor Matias Navarro <victor.matias.navarro@gmail.com>
Showing
13 changed files
with
383 additions
and
12 deletions
Show diff stats
controllers/software_communities_plugin_profile_controller.rb
0 → 100644
@@ -0,0 +1,49 @@ | @@ -0,0 +1,49 @@ | ||
1 | +class SoftwareCommunitiesPluginProfileController < ProfileController | ||
2 | + append_view_path File.join(File.dirname(__FILE__) + '/../views') | ||
3 | + | ||
4 | + before_filter :validate_download_params, only: [:download_file] | ||
5 | + | ||
6 | + ERROR_MESSAGES = { | ||
7 | + :not_found => _("Could not find the download file"), | ||
8 | + :invalid_params => _("Invalid download params") | ||
9 | + } | ||
10 | + | ||
11 | + def download_file | ||
12 | + download_block = DownloadBlock.find_by_id params[:block] | ||
13 | + index = params[:download_index].to_i | ||
14 | + | ||
15 | + if download_block and (index < download_block.downloads.size) | ||
16 | + download = Download.new(download_block.downloads[index]) | ||
17 | + | ||
18 | + download.total_downloads += 1 | ||
19 | + download_block.downloads[index] = download.to_hash | ||
20 | + download_block.save | ||
21 | + | ||
22 | + redirect_to download.link | ||
23 | + else | ||
24 | + session[:notice] = ERROR_MESSAGES[:not_found] | ||
25 | + render_not_found | ||
26 | + end | ||
27 | + end | ||
28 | + | ||
29 | + private | ||
30 | + | ||
31 | + def validate_download_params | ||
32 | + valid_block = (!params[:block].nil?) and (params[:block].to_i > 0) | ||
33 | + valid_index = params[:download_index].to_i >= 0 | ||
34 | + | ||
35 | + if !valid_block or !valid_index | ||
36 | + session[:notice] = ERROR_MESSAGES[:invalid_params] | ||
37 | + safe_redirect_back | ||
38 | + end | ||
39 | + end | ||
40 | + | ||
41 | + def safe_redirect_back | ||
42 | + begin | ||
43 | + redirect_to :back | ||
44 | + rescue ActionController::RedirectBackError | ||
45 | + # There is no :back if it is a copied url | ||
46 | + render_not_found | ||
47 | + end | ||
48 | + end | ||
49 | +end |
@@ -0,0 +1,51 @@ | @@ -0,0 +1,51 @@ | ||
1 | +#FIX ME: Turn this into a proper model(next release) | ||
2 | +class Download | ||
3 | + def initialize data | ||
4 | + @name = data[:name] | ||
5 | + @link = data[:link] | ||
6 | + @software_description = data[:software_description] | ||
7 | + @minimum_requirements = data[:minimum_requirements] | ||
8 | + @size = data[:size] | ||
9 | + | ||
10 | + @total_downloads = if data[:total_downloads] | ||
11 | + data[:total_downloads] | ||
12 | + else | ||
13 | + 0 | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + def self.validate_download_list download_list | ||
18 | + download_list.select! do |download| | ||
19 | + not download[:name].blank? | ||
20 | + end | ||
21 | + | ||
22 | + download_list.map do |download| | ||
23 | + Download.new(download).to_hash | ||
24 | + end | ||
25 | + end | ||
26 | + | ||
27 | + def to_hash | ||
28 | + { | ||
29 | + :name => @name, | ||
30 | + :link => @link, | ||
31 | + :software_description => @software_description, | ||
32 | + :minimum_requirements => @minimum_requirements, | ||
33 | + :size => @size, | ||
34 | + :total_downloads => @total_downloads | ||
35 | + } | ||
36 | + end | ||
37 | + | ||
38 | + def total_downloads= value | ||
39 | + if value.is_a? Integer | ||
40 | + @total_downloads = value | ||
41 | + end | ||
42 | + end | ||
43 | + | ||
44 | + def total_downloads | ||
45 | + @total_downloads | ||
46 | + end | ||
47 | + | ||
48 | + def link | ||
49 | + @link | ||
50 | + end | ||
51 | +end |
lib/download_block.rb
@@ -8,11 +8,7 @@ class DownloadBlock < Block | @@ -8,11 +8,7 @@ class DownloadBlock < Block | ||
8 | validate :download_values | 8 | validate :download_values |
9 | 9 | ||
10 | def download_values | 10 | def download_values |
11 | - self.downloads.each do |download| | ||
12 | - if download[:name] == "" | ||
13 | - downloads.delete(download) | ||
14 | - end | ||
15 | - end | 11 | + self.downloads = Download.validate_download_list(self.downloads) |
16 | end | 12 | end |
17 | 13 | ||
18 | def self.description | 14 | def self.description |
lib/ext/community.rb
@@ -12,6 +12,8 @@ class Community | @@ -12,6 +12,8 @@ class Community | ||
12 | 12 | ||
13 | has_one :software_info, :dependent=>:destroy | 13 | has_one :software_info, :dependent=>:destroy |
14 | 14 | ||
15 | + settings_items :hits, :type => :integer, :default => 0 | ||
16 | + | ||
15 | def self.create_after_moderation(requestor, attributes = {}) | 17 | def self.create_after_moderation(requestor, attributes = {}) |
16 | community = Community.new(attributes) | 18 | community = Community.new(attributes) |
17 | 19 | ||
@@ -55,4 +57,10 @@ class Community | @@ -55,4 +57,10 @@ class Community | ||
55 | def remove_of_community_search_software? | 57 | def remove_of_community_search_software? |
56 | return software? | 58 | return software? |
57 | end | 59 | end |
60 | + | ||
61 | + def hit | ||
62 | + self.hits += 1 | ||
63 | + self.save! | ||
64 | + end | ||
65 | + | ||
58 | end | 66 | end |
lib/ext/profile_controller.rb
@@ -2,6 +2,8 @@ require_dependency 'profile_controller' | @@ -2,6 +2,8 @@ require_dependency 'profile_controller' | ||
2 | 2 | ||
3 | class ProfileController | 3 | class ProfileController |
4 | 4 | ||
5 | + before_filter :hit_view_page | ||
6 | + | ||
5 | def communities | 7 | def communities |
6 | type = [] | 8 | type = [] |
7 | params[:type].downcase! unless params[:type].nil? | 9 | params[:type].downcase! unless params[:type].nil? |
@@ -37,4 +39,33 @@ class ProfileController | @@ -37,4 +39,33 @@ class ProfileController | ||
37 | end | 39 | end |
38 | end | 40 | end |
39 | 41 | ||
42 | + def user_is_a_bot? | ||
43 | + user_agent= request.env["HTTP_USER_AGENT"] | ||
44 | + user_agent.blank? || | ||
45 | + user_agent.match(/bot/) || | ||
46 | + user_agent.match(/spider/) || | ||
47 | + user_agent.match(/crawler/) || | ||
48 | + user_agent.match(/\(.*https?:\/\/.*\)/) | ||
49 | + end | ||
50 | + | ||
51 | + def already_visited?(element) | ||
52 | + user_id = if user.nil? then -1 else current_user.id end | ||
53 | + user_id = "#{user_id}_#{element.id}_#{element.class}" | ||
54 | + | ||
55 | + if cookies.signed[:visited] == user_id | ||
56 | + return true | ||
57 | + else | ||
58 | + cookies.permanent.signed[:visited] = user_id | ||
59 | + return false | ||
60 | + end | ||
61 | + end | ||
62 | + | ||
63 | + def hit_view_page | ||
64 | + if profile | ||
65 | + community = profile | ||
66 | + community.hit unless user_is_a_bot? || | ||
67 | + already_visited?(community) || | ||
68 | + community.class != Community | ||
69 | + end | ||
70 | + end | ||
40 | end | 71 | end |
lib/software_communities_plugin.rb
@@ -42,7 +42,8 @@ class SoftwareCommunitiesPlugin < Noosfero::Plugin | @@ -42,7 +42,8 @@ class SoftwareCommunitiesPlugin < Noosfero::Plugin | ||
42 | SearchCatalogBlock => { :type => [Environment] }, | 42 | SearchCatalogBlock => { :type => [Environment] }, |
43 | SoftwareHighlightsBlock => { :type => [Environment] }, | 43 | SoftwareHighlightsBlock => { :type => [Environment] }, |
44 | SoftwareTabDataBlock => {:type => [Community], :position => 1}, | 44 | SoftwareTabDataBlock => {:type => [Community], :position => 1}, |
45 | - WikiBlock => {:type => [Community]} | 45 | + WikiBlock => {:type => [Community]}, |
46 | + StatisticBlock => { :type => [Community] } | ||
46 | } | 47 | } |
47 | end | 48 | end |
48 | 49 |
@@ -0,0 +1,52 @@ | @@ -0,0 +1,52 @@ | ||
1 | +class StatisticBlock < Block | ||
2 | + | ||
3 | + settings_items :benefited_people, :type => :integer, :default => 0 | ||
4 | + settings_items :saved_resources, :type => :float, :default => 0.0 | ||
5 | + | ||
6 | + attr_accessible :benefited_people, :saved_resources | ||
7 | + | ||
8 | + def self.description | ||
9 | + _('Software Statistics') | ||
10 | + end | ||
11 | + | ||
12 | + def help | ||
13 | + _('This block displays software statistics.') | ||
14 | + end | ||
15 | + | ||
16 | + def content(args={}) | ||
17 | + download_blocks = get_profile_download_blocks(self.owner) | ||
18 | + downloads = download_blocks.map do |download_block| | ||
19 | + get_downloads_from_block(download_block) | ||
20 | + end | ||
21 | + | ||
22 | + block = self | ||
23 | + | ||
24 | + lambda do |object| | ||
25 | + render( | ||
26 | + :file => 'blocks/software_statistics', | ||
27 | + :locals => { | ||
28 | + :block => block, | ||
29 | + :total_downloads => downloads.sum | ||
30 | + } | ||
31 | + ) | ||
32 | + end | ||
33 | + end | ||
34 | + | ||
35 | + def cacheable? | ||
36 | + false | ||
37 | + end | ||
38 | + | ||
39 | + private | ||
40 | + | ||
41 | + def get_profile_download_blocks profile | ||
42 | + DownloadBlock.joins(:box).where("boxes.owner_id = ?", profile.id) | ||
43 | + end | ||
44 | + | ||
45 | + def get_downloads_from_block download_block | ||
46 | + downloads = download_block.downloads.map do |download| | ||
47 | + download[:total_downloads] unless download[:total_downloads].nil? | ||
48 | + end | ||
49 | + downloads.select! {|value| not value.nil? } | ||
50 | + downloads.sum | ||
51 | + end | ||
52 | +end |
@@ -0,0 +1,40 @@ | @@ -0,0 +1,40 @@ | ||
1 | +require "test_helper" | ||
2 | +require 'profile_controller' | ||
3 | + | ||
4 | +# Re-raise errors caught by the controller. | ||
5 | +class ProfileController; def rescue_action(e) raise e end; end | ||
6 | + | ||
7 | +class ProfileControllerTest < ActionController::TestCase | ||
8 | + def setup | ||
9 | + Environment.default.enable('products_for_enterprises') | ||
10 | + @profile = create_user('testuser').person | ||
11 | + end | ||
12 | + attr_reader :profile | ||
13 | + | ||
14 | + should 'not count a visit twice for the same user' do | ||
15 | + profile = create_user('someone').person | ||
16 | + login_as(@profile.identifier) | ||
17 | + community = fast_create('Community') | ||
18 | + | ||
19 | + get :index, :profile => community.identifier | ||
20 | + community.reload | ||
21 | + assert_equal 1, community.hits | ||
22 | + | ||
23 | + get :index, :profile => community.identifier | ||
24 | + community.reload | ||
25 | + assert_equal 1, community.hits | ||
26 | + end | ||
27 | + | ||
28 | + should 'not count a visit twice for unlogged users' do | ||
29 | + profile = create_user('someone').person | ||
30 | + community = fast_create('Community') | ||
31 | + get :index, :profile => community.identifier | ||
32 | + community.reload | ||
33 | + assert_equal 1, community.hits | ||
34 | + | ||
35 | + get :index, :profile => community.identifier | ||
36 | + community.reload | ||
37 | + assert_equal 1, community.hits | ||
38 | + end | ||
39 | +end | ||
40 | + |
test/functional/software_communities_plugin_profile_controller_test.rb
0 → 100644
@@ -0,0 +1,67 @@ | @@ -0,0 +1,67 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
2 | +require File.dirname(__FILE__) + '/../helpers/software_test_helper' | ||
3 | +require File.dirname(__FILE__) + '/../../controllers/software_communities_plugin_profile_controller' | ||
4 | + | ||
5 | +class SoftwareCommunitiesPluginProfileController; def rescue_action(e) raise e end; end | ||
6 | + | ||
7 | +class SoftwareCommunitiesPluginProfileControllerTest < ActionController::TestCase | ||
8 | + include SoftwareTestHelper | ||
9 | + | ||
10 | + def setup | ||
11 | + @controller = SoftwareCommunitiesPluginProfileController.new | ||
12 | + @request = ActionController::TestRequest.new | ||
13 | + @response = ActionController::TestResponse.new | ||
14 | + | ||
15 | + @environment = Environment.default | ||
16 | + @environment.enable_plugin('SoftwareCommunitiesPlugin') | ||
17 | + @environment.save! | ||
18 | + | ||
19 | + LicenseInfo.create( | ||
20 | + :version=>"CC-GPL-V2", | ||
21 | + :link=>"http://creativecommons.org/licenses/GPL/2.0/legalcode.pt" | ||
22 | + ) | ||
23 | + @download_data = { | ||
24 | + :name=>"Google", | ||
25 | + :link=>"http://google.com", | ||
26 | + :software_description=>"all", | ||
27 | + :minimum_requirements=>"none", | ||
28 | + :size=>"?", | ||
29 | + :total_downloads=>0 | ||
30 | + } | ||
31 | + | ||
32 | + @software = create_software(software_fields) | ||
33 | + @software.save! | ||
34 | + | ||
35 | + download_block = DownloadBlock.new | ||
36 | + download_block.downloads = Download.validate_download_list([@download_data]) | ||
37 | + download_block.save! | ||
38 | + | ||
39 | + @software.community.blocks << download_block | ||
40 | + @software.community.save! | ||
41 | + end | ||
42 | + | ||
43 | + should 'redirect to download link with correct params' do | ||
44 | + download_block = DownloadBlock.last | ||
45 | + get :download_file, :profile=>@software.community.identifier, | ||
46 | + :block => download_block.id, :download_index => 0 | ||
47 | + | ||
48 | + assert_equal nil, session[:notice] | ||
49 | + assert_redirected_to download_block.downloads[0][:link] | ||
50 | + end | ||
51 | + | ||
52 | + should "notice when the download was not found" do | ||
53 | + download_block = DownloadBlock.last | ||
54 | + get :download_file, :profile=>@software.community.identifier, | ||
55 | + :block => 123, :download_index => 0 | ||
56 | + | ||
57 | + assert_equal "Could not find the download file", session[:notice] | ||
58 | + end | ||
59 | + | ||
60 | + should "notice when given invalid download params" do | ||
61 | + download_block = DownloadBlock.last | ||
62 | + get :download_file, :profile=>@software.community.identifier, | ||
63 | + :block => download_block.id, :download_index => -5 | ||
64 | + | ||
65 | + assert_equal "Invalid download params", session[:notice] | ||
66 | + end | ||
67 | +end |
@@ -0,0 +1,53 @@ | @@ -0,0 +1,53 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
2 | +require File.dirname(__FILE__) + '/../helpers/plugin_test_helper' | ||
3 | + | ||
4 | +class DownloadTest < ActiveSupport::TestCase | ||
5 | + include PluginTestHelper | ||
6 | + | ||
7 | + def setup | ||
8 | + @downloads_sample_data = [ | ||
9 | + { | ||
10 | + :name=>"Sample data A", | ||
11 | + :link=>"http://some.url.com", | ||
12 | + :software_description=>"all", | ||
13 | + :minimum_requirements=>"none", | ||
14 | + :size=>"10 mb", | ||
15 | + :total_downloads=>0 | ||
16 | + }, | ||
17 | + { | ||
18 | + :name=>"Sample data B", | ||
19 | + :link=>"http://other.url", | ||
20 | + :software_description=>"Linux", | ||
21 | + :minimum_requirements=>"500 mb Ram", | ||
22 | + :size=>"3 GB", | ||
23 | + :total_downloads=>123 | ||
24 | + } | ||
25 | + ] | ||
26 | + end | ||
27 | + | ||
28 | + should "Set as 0(zero) total_downloads if it is not given" do | ||
29 | + without_total_downloads = Download.new({}) | ||
30 | + with_total_downloads = Download.new(@downloads_sample_data.last) | ||
31 | + | ||
32 | + assert_equal 0, without_total_downloads.total_downloads | ||
33 | + assert_equal @downloads_sample_data.last[:total_downloads], with_total_downloads.total_downloads | ||
34 | + end | ||
35 | + | ||
36 | + should "Remove downloads without a name" do | ||
37 | + @downloads_sample_data[1] = @downloads_sample_data[1].slice! :name | ||
38 | + downloads = Download.validate_download_list @downloads_sample_data | ||
39 | + | ||
40 | + assert_equal 1, downloads.size | ||
41 | + assert_equal @downloads_sample_data[0][:name], downloads[0][:name] | ||
42 | + end | ||
43 | + | ||
44 | + should "Only set total_downloads if the value is integer" do | ||
45 | + download = Download.new(@downloads_sample_data.last) | ||
46 | + | ||
47 | + download.total_downloads = "456" | ||
48 | + assert_not_equal 456, download.total_downloads | ||
49 | + | ||
50 | + download.total_downloads = 456 | ||
51 | + assert_equal 456, download.total_downloads | ||
52 | + end | ||
53 | +end |
views/blocks/download.html.erb
@@ -4,12 +4,19 @@ | @@ -4,12 +4,19 @@ | ||
4 | <h3 class="block-title"> <%= _("Download #{block.owner.software_info.community.name}") %> </h3> | 4 | <h3 class="block-title"> <%= _("Download #{block.owner.software_info.community.name}") %> </h3> |
5 | <ul class="download-list"> | 5 | <ul class="download-list"> |
6 | <% block.downloads.each_with_index do |download, index| %> | 6 | <% block.downloads.each_with_index do |download, index| %> |
7 | - <li id="download-item-<%=(index+1)%>"> | ||
8 | - <div class="download-button"> | ||
9 | - <%= link_to download[:link], title: _("Download the software") do %> | ||
10 | - <span class="download-image"></span> | ||
11 | - <span class="download-size"><%= download[:size] %></span> | ||
12 | - <% end %> | 7 | + <div id="download-info-<%=(index+1)%>"> |
8 | + <div id="version01"> | ||
9 | + <%= link_to :controller => 'software_communities_plugin_profile', :action=> 'download_file', :block=>block.id, :download_index=> index , title: _("Download the software") do %> | ||
10 | + <span id="image-download"></span> | ||
11 | + <span id="size-download"><%= download[:size] %></span> | ||
12 | + <% end %> | ||
13 | + </div> | ||
14 | + | ||
15 | + <div id="info-software-download"> | ||
16 | + <span class="download-name"><%= _("#{download[:name]}") %></span> | ||
17 | + <span class="download-system"><%= _("Platform:#{download[:software_description]}") %> </span> | ||
18 | + <span class="req_min_spb"><%= link_to _("Minimum Requirements"), download[:minimum_requirements] %></span> | ||
19 | + </div> | ||
13 | </div> | 20 | </div> |
14 | <div class="download-info"> | 21 | <div class="download-info"> |
15 | <span class="download-name"><%= _("#{download[:name]}") %></span> | 22 | <span class="download-name"><%= _("#{download[:name]}") %></span> |
@@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
1 | +<div class="software-metrics-block"> | ||
2 | + <ul class="metrics-list"> | ||
3 | + <li><span class="arrow-globe-icon"></span><a href="#"><%= pluralize(profile.hits, 'visita', 'visitas') %></a></li> | ||
4 | + <li><span class="downloads-icon"></span><a href="#"><%= pluralize(total_downloads, 'download', 'downloads') %></a></li> | ||
5 | + <li><span class="face-icon"></span><a href="#"><%= block.benefited_people.to_s + _(' benefited people*') %></a></li> | ||
6 | + <li><span class="pig-safe-icon"></span><a href="#"><strong class="saved-money"><%= number_to_currency(block.saved_resources, unit: 'R$ ', separator: ',', delimiter: '.') %></strong> <%= _(' saved resources*') %></a></li> | ||
7 | + </ul> | ||
8 | + | ||
9 | + | ||
10 | + <div class="admin-estimation">* <%= _("Data estimated by the software administrator.") %></div> | ||
11 | +</div> |