Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add superspace statistics #27

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
3 changes: 2 additions & 1 deletion app/commands/decidim/superspaces/admin/create_superspace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def create_superspace!
title: form.title,
description: form.description,
locale: form.locale,
hero_image: form.hero_image
hero_image: form.hero_image,
show_statistics: form.show_statistics
}

@superspace = Decidim.traceability.create!(
Expand Down
3 changes: 2 additions & 1 deletion app/commands/decidim/superspaces/admin/update_superspace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def update_superspace!
title: form.title,
description: form.description,
hero_image: form.hero_image,
locale: form.locale
locale: form.locale,
show_statistics: form.show_statistics
)
update_associations(assembly_ids, participatory_process_ids, conference_ids)
end
Expand Down
1 change: 1 addition & 0 deletions app/forms/decidim/superspaces/admin/superspace_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class SuperspaceForm < Decidim::Form
attribute :assembly_ids
attribute :participatory_process_ids
attribute :conference_ids
attribute :show_statistics, Boolean

validates :title, translatable_presence: true
validates :locale, presence: true
Expand Down
4 changes: 4 additions & 0 deletions app/models/decidim/superspaces/superspace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def conferences
find_spaces_by_type("Decidim::Conference")
end

def statistics
Decidim::Superspaces::SuperspaceStatsPresenter.new(self).collection
end

def self.log_presenter_class_for(_log) = Decidim::Superspaces::AdminLog::SuperspacePresenter

private
Expand Down
35 changes: 35 additions & 0 deletions app/presenters/decidim/superspaces/superspace_stats_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Decidim
module Superspaces
# This class holds the logic to present superspace stats.
# It inherits from `Decidim::StatsPresenter` and overrides the methods
# needed to adapt the stats to the superspace context.

class SuperspaceStatsPresenter < Decidim::StatsPresenter
include Decidim::IconHelper

private

def participatory_space = __getobj__

def participatory_processes
@participatory_processes ||= participatory_space.participatory_processes + participatory_space.assemblies + participatory_space.conferences
end

def participatory_space_participants_stats
Decidim::Superspaces::StatsParticipantsCount.for(participatory_space)
end

def participatory_space_followers_stats(_conditions)
Decidim::Superspaces::StatsFollowersCount.for(participatory_space)
end

def published_components
@published_components ||= Component.where(participatory_space: participatory_processes).published
end

def participatory_space_sym = :superspace
end
end
end
62 changes: 62 additions & 0 deletions app/queries/decidim/superspaces/stats_followers_count.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

module Decidim
module Superspaces
class StatsFollowersCount < Decidim::Query
# This class is responsible for calculating the number of followers of a superspace.
# The number of followers in a superspace is equal to the sum of the number of followers in all the participatory spaces that belong to the superspace.

def self.for(superspace)
return 0 unless superspace.is_a?(Decidim::Superspaces::Superspace)

new(superspace).query
end

def initialize(superspace)
@superspace = superspace
end

def query
count = space_query + components_query

data = [{ participatory_space: superspace.to_s, stat_title: "followers_count", stat_value: count }]

data.map do |d|
[d[:participatory_space].to_sym, d[:stat_title].to_sym, d[:stat_value].to_i]
end
end

private

attr_reader :superspace

def components_query
Decidim.component_manifests.sum do |component|
component.stats
.filter(tag: :followers)
.with_context(space_components)
.map { |_name, value| value }
.sum
end
end

def space_query
Decidim.participatory_space_manifests.sum do |space|
space.stats
.filter(tag: :followers)
.with_context(participatory_space_items)
.map { |_name, value| value }
.sum
end
end

def participatory_space_items
@participatory_space_items ||= superspace.participatory_spaces
end

def space_components
@space_components ||= Decidim::Component.where(participatory_space: participatory_space_items).published
end
end
end
end
177 changes: 177 additions & 0 deletions app/queries/decidim/superspaces/stats_participants_count.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# frozen_string_literal: true

module Decidim
module Superspaces
class StatsParticipantsCount < Decidim::Query
# This class is responsible for calculating the number of participants of a superspace.
# The number of participants in a superspace is equal to the sum of participants of the participatory spaces that belong to the superspace.
# If a user has participated in more than one participatory space, it will only be counted once.

def self.for(superspace)
return 0 unless superspace.is_a?(Decidim::Superspaces::Superspace)

new(superspace).query
end

def initialize(superspace)
@superspace = superspace
end

def query
participatory_process_class_name = Decidim::ParticipatoryProcess.class.name
assemblies_class_name = Decidim::Assembly.class.name
conferences_class_name = Decidim::Conference.class.name

solution = [
comments_query(participatory_process_class_name, participatory_space_ids),
comments_query(assemblies_class_name, assemblies_ids),
comments_query(conferences_class_name, conferences_ids),
debates_query(space_components),
debates_query(assemblies_components),
debates_query(conferences_components),
meetings_query(space_components),
meetings_query(assemblies_components),
meetings_query(conferences_components),
endorsements_query(space_components),
endorsements_query(assemblies_components),
endorsements_query(conferences_components),
project_votes_query(space_components),
project_votes_query(assemblies_components),
project_votes_query(conferences_components),
proposals_query(proposals_components),
proposals_query(assemblies_proposals_components),
proposals_query(conferences_proposals_components),
proposal_votes_query(proposals_components),
proposal_votes_query(assemblies_proposals_components),
proposal_votes_query(conferences_proposals_components),
survey_answer_query(space_components),
survey_answer_query(assemblies_components),
survey_answer_query(conferences_components)
].flatten.uniq.count

data = [{ participatory_space: @superspace.to_s, stat_title: "participants_count", stat_value: solution }]

data.map do |d|
[d[:participatory_space].to_sym, d[:stat_title].to_sym, d[:stat_value].to_i]
end
end

private

def participatory_space_ids
@participatory_space_ids ||= @superspace.participatory_processes.map(&:id)
end

def assemblies_ids
@assemblies_ids ||= @superspace.assemblies.map(&:id)
end

def conferences_ids
@conferences_ids ||= @superspace.conferences.map(&:id)
end

def comments_query(class_name, ids)
return [] unless Decidim.module_installed?(:comments)

Decidim::Comments::Comment
.where(decidim_participatory_space_type: class_name)
.where(decidim_participatory_space_id: ids)
.pluck(:decidim_author_id)
.uniq
end

def debates_query(components)
return [] unless Decidim.module_installed?(:debates)

Decidim::Debates::Debate
.where(
component: components,
decidim_author_type: Decidim::UserBaseEntity.name
)
.not_hidden
.pluck(:decidim_author_id)
.uniq
end

def meetings_query(components)
return [] unless Decidim.module_installed?(:meetings)

meetings = Decidim::Meetings::Meeting.where(component: components).not_hidden
registrations = Decidim::Meetings::Registration.where(decidim_meeting_id: meetings).distinct.pluck(:decidim_user_id)
organizers = meetings.where(decidim_author_type: Decidim::UserBaseEntity.name).distinct.pluck(:decidim_author_id)

[registrations, organizers].flatten.uniq
end

def endorsements_query(components)
Decidim::Endorsement
.where(resource: components)
.pluck(:decidim_author_id)
.uniq
end

def proposals_query(proposals_components)
return [] unless Decidim.module_installed?(:proposals)

Decidim::Coauthorship
.where(
coauthorable: proposals_components,
decidim_author_type: Decidim::UserBaseEntity.name
)
.pluck(:decidim_author_id)
.uniq
end

def proposal_votes_query(proposals_components)
return [] unless Decidim.module_installed?(:proposals)

Decidim::Proposals::ProposalVote
.where(
proposal: proposals_components
)
.final
.pluck(:decidim_author_id)
.uniq
end

def project_votes_query(components)
return [] unless Decidim.module_installed?(:budgets)

Decidim::Budgets::Order.joins(budget: [:component])
.where(budget: {
decidim_components: { id: components.pluck(:id) }
})
.pluck(:decidim_user_id)
.uniq
end

def survey_answer_query(components)
Decidim::Forms::Answer.newsletter_participant_ids(components)
end

def space_components
Decidim::Component.where(participatory_space: @superspace.participatory_processes)
end

def assemblies_components
Decidim::Component.where(participatory_space: @superspace.assemblies)
end

def conferences_components
Decidim::Component.where(participatory_space: @superspace.conferences)
end

def proposals_components
@proposals_components ||= Decidim::Proposals::FilteredProposals.for(space_components).published.not_hidden
end

def assemblies_proposals_components
@assemblies_proposals_components ||= Decidim::Proposals::FilteredProposals.for(assemblies_components).published.not_hidden
end

def conferences_proposals_components
@conferences_proposals_components ||= Decidim::Proposals::FilteredProposals.for(conferences_components).published.not_hidden
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
</fieldset>
</div>
</div>
<div class="row column">
<%= form.check_box :show_statistics %>
</div>
</div>
</div>
</div>
7 changes: 7 additions & 0 deletions app/views/decidim/superspaces/superspaces/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@
<% end %>
</section>
<% end %>

<% if superspace.show_statistics %>
<section id="statistics-grid" class="layout-main__section layout-main__heading">
<h2 class="title-decorator"><%= t("statistics", scope: "decidim.superspaces.superspaces.show") %></h2>
<%= cell("decidim/statistics",superspace.statistics) %>
</section>
<% end %>
<% end %>

<%= render partial: "language_selector" %>
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ en:
show:
assemblies: Assemblies
participatory_processes: Participatory Processes
statistics: Statistics
1 change: 1 addition & 0 deletions config/locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ es:
show:
assemblies: Asambleas
participatory_processes: Procesos participativos
statistics: Estadísticas
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddShowStatisticsToDecidimSuperspacesSuperspaces < ActiveRecord::Migration[6.1]
def change
add_column :decidim_superspaces_superspaces, :show_statistics, :boolean
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module Admin
let(:participatory_process_ids) { nil }
let(:conference_ids) { nil }
let(:invalid) { false }
let(:show_statistics) { false }
let(:form) do
double(
invalid?: invalid,
Expand All @@ -28,7 +29,8 @@ module Admin
locale:,
assembly_ids:,
participatory_process_ids:,
conference_ids:
conference_ids:,
show_statistics:
)
end

Expand Down
Loading