decidim-results 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +23 -0
  3. data/Rakefile +2 -0
  4. data/app/assets/images/decidim/results/icon.svg +1 -0
  5. data/app/commands/decidim/results/admin/create_result.rb +65 -0
  6. data/app/commands/decidim/results/admin/update_result.rb +69 -0
  7. data/app/controllers/decidim/results/admin/application_controller.rb +14 -0
  8. data/app/controllers/decidim/results/admin/results_controller.rb +69 -0
  9. data/app/controllers/decidim/results/application_controller.rb +13 -0
  10. data/app/controllers/decidim/results/results_controller.rb +42 -0
  11. data/app/forms/decidim/results/admin/result_form.rb +40 -0
  12. data/app/helpers/decidim/results/application_helper.rb +11 -0
  13. data/app/models/decidim/results/application_record.rb +9 -0
  14. data/app/models/decidim/results/result.rb +15 -0
  15. data/app/services/decidim/results/result_search.rb +44 -0
  16. data/app/services/decidim/results/result_stats_calculator.rb +51 -0
  17. data/app/views/decidim/results/admin/results/_form.html.erb +23 -0
  18. data/app/views/decidim/results/admin/results/edit.html.erb +9 -0
  19. data/app/views/decidim/results/admin/results/index.html.erb +27 -0
  20. data/app/views/decidim/results/admin/results/new.html.erb +9 -0
  21. data/app/views/decidim/results/results/_count.html.erb +1 -0
  22. data/app/views/decidim/results/results/_filters.html.erb +22 -0
  23. data/app/views/decidim/results/results/_linked_results.html.erb +12 -0
  24. data/app/views/decidim/results/results/_results.html.erb +16 -0
  25. data/app/views/decidim/results/results/_tags.html.erb +10 -0
  26. data/app/views/decidim/results/results/index.html.erb +22 -0
  27. data/app/views/decidim/results/results/index.js.erb +2 -0
  28. data/app/views/decidim/results/results/show.html.erb +52 -0
  29. data/config/i18n-tasks.yml +5 -0
  30. data/config/locales/ca.yml +61 -0
  31. data/config/locales/en.yml +62 -0
  32. data/config/locales/es.yml +61 -0
  33. data/db/migrate/20170116104125_create_results.rb +14 -0
  34. data/db/migrate/20170129164553_remove_short_description_from_results.rb +5 -0
  35. data/lib/decidim/results/admin.rb +9 -0
  36. data/lib/decidim/results/admin_engine.rb +22 -0
  37. data/lib/decidim/results/feature.rb +52 -0
  38. data/lib/decidim/results/list_engine.rb +19 -0
  39. data/lib/decidim/results/test/factories.rb +11 -0
  40. data/lib/decidim/results.rb +11 -0
  41. metadata +213 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6b31f33002d2e563b304b59e39a656bd5f8cce64
4
+ data.tar.gz: f38e1469828e9db0a67349ddc217a734d7c0bb64
5
+ SHA512:
6
+ metadata.gz: a18ff00b0d57bcaed24fbc52df0f50cbb5d8bf7c457b0651588bd70156411d28e5eab45ccd3295d53c261e213faf11ce3408a27fd06d4ebbb9d08761c25f2cce
7
+ data.tar.gz: e3e967335b57634c6ab80b73c4d9abfe37d92350f0b00cb007e9ef9512aa33ca4cbfd627c4ba029f638aee263a9ebc14c6d3eb0388de1b4792525f03b5f8c2cd
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # Decidim::Results
2
+ The Results module adds results to any participatory process. It adds a CRUD engine to the admin and public views scoped inside the participatory process. Results will link to related meetings and proposals and will be used to show the progress on the related proposals.
3
+
4
+ ## Usage
5
+ results will be available as a Feature for a Participatory Process.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'decidim-results
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ ## Contributing
20
+ See [Decidim](https://github.com/AjuntamentdeBarcelona/decidim).
21
+
22
+ ## License
23
+ See [Decidim](https://github.com/AjuntamentdeBarcelona/decidim).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require "decidim/dev/common_rake"
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35"><path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zM16 22.48a1 1 0 0 1-.68-.28l-4.08-4a1 1 0 0 1 1.36-1.39l3.4 3.3 8-8.8a1 1 0 0 1 1.44 1.31l-8.69 9.55a1 1 0 0 1-.69.32z"/></svg>
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ module Admin
5
+ # This command is executed when the user creates a Result from the admin
6
+ # panel.
7
+ class CreateResult < Rectify::Command
8
+ def initialize(form)
9
+ @form = form
10
+ end
11
+
12
+ # Creates the result if valid.
13
+ #
14
+ # Broadcasts :ok if successful, :invalid otherwise.
15
+ def call
16
+ return broadcast(:invalid) if @form.invalid?
17
+
18
+ transaction do
19
+ create_result
20
+ link_meetings
21
+ link_proposals
22
+ end
23
+
24
+ broadcast(:ok)
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :result
30
+
31
+ def create_result
32
+ @result = Result.create!(
33
+ scope: @form.scope,
34
+ category: @form.category,
35
+ feature: @form.current_feature,
36
+ title: @form.title,
37
+ description: @form.description
38
+ )
39
+ end
40
+
41
+ def proposals
42
+ @proposals ||= result.sibling_scope(:proposals).where(id: @form.proposal_ids)
43
+ end
44
+
45
+ def meeting_ids
46
+ @meeting_ids ||= proposals.flat_map do |proposal|
47
+ proposal.linked_resources(:meetings, "proposals_from_meeting").pluck(:id)
48
+ end.uniq
49
+ end
50
+
51
+ def meetings
52
+ @meetings ||= result.sibling_scope(:meetings).where(id: meeting_ids)
53
+ end
54
+
55
+ def link_proposals
56
+ result.link_resources(proposals, "included_proposals")
57
+ end
58
+
59
+ def link_meetings
60
+ result.link_resources(meetings, "meetings_through_proposals")
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ module Admin
5
+ # This command is executed when the user changes a Result from the admin
6
+ # panel.
7
+ class UpdateResult < Rectify::Command
8
+ # Initializes an UpdateResult Command.
9
+ #
10
+ # form - The form from which to get the data.
11
+ # result - The current instance of the result to be updated.
12
+ def initialize(form, result)
13
+ @form = form
14
+ @result = result
15
+ end
16
+
17
+ # Updates the result if valid.
18
+ #
19
+ # Broadcasts :ok if successful, :invalid otherwise.
20
+ def call
21
+ return broadcast(:invalid) if form.invalid?
22
+
23
+ transaction do
24
+ update_result
25
+ link_proposals
26
+ link_meetings
27
+ end
28
+
29
+ broadcast(:ok)
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :result, :form
35
+
36
+ def update_result
37
+ result.update_attributes!(
38
+ scope: form.scope,
39
+ category: form.category,
40
+ title: form.title,
41
+ description: form.description
42
+ )
43
+ end
44
+
45
+ def proposals
46
+ @proposals ||= result.sibling_scope(:proposals).where(id: form.proposal_ids)
47
+ end
48
+
49
+ def meeting_ids
50
+ @meeting_ids ||= proposals.flat_map do |proposal|
51
+ proposal.linked_resources(:meetings, "proposals_from_meeting").pluck(:id)
52
+ end.uniq
53
+ end
54
+
55
+ def meetings
56
+ @meetings ||= result.sibling_scope(:meetings).where(id: meeting_ids)
57
+ end
58
+
59
+ def link_proposals
60
+ result.link_resources(proposals, "included_proposals")
61
+ end
62
+
63
+ def link_meetings
64
+ result.link_resources(meetings, "meetings_through_proposals")
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ module Admin
5
+ # This controller is the abstract class from which all other controllers of
6
+ # this engine inherit.
7
+ #
8
+ # Note that it inherits from `Decidim::Features::BaseController`, which
9
+ # override its layout and provide all kinds of useful methods.
10
+ class ApplicationController < Decidim::Admin::Features::BaseController
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ module Admin
5
+ # This controller allows an admin to manage results from a Participatory Process
6
+ class ResultsController < Admin::ApplicationController
7
+ helper_method :results
8
+
9
+ def new
10
+ @form = form(ResultForm).instance
11
+ end
12
+
13
+ def create
14
+ @form = form(ResultForm).from_params(params)
15
+
16
+ CreateResult.call(@form) do
17
+ on(:ok) do
18
+ flash[:notice] = I18n.t("results.create.success", scope: "decidim.results.admin")
19
+ redirect_to results_path
20
+ end
21
+
22
+ on(:invalid) do
23
+ flash.now[:alert] = I18n.t("results.create.invalid", scope: "decidim.results.admin")
24
+ render action: "new"
25
+ end
26
+ end
27
+ end
28
+
29
+ def edit
30
+ @form = form(ResultForm).from_model(result)
31
+ end
32
+
33
+ def update
34
+ @form = form(ResultForm).from_params(params)
35
+
36
+ UpdateResult.call(@form, result) do
37
+ on(:ok) do
38
+ flash[:notice] = I18n.t("results.update.success", scope: "decidim.results.admin")
39
+ redirect_to results_path
40
+ end
41
+
42
+ on(:invalid) do
43
+ flash.now[:alert] = I18n.t("results.update.invalid", scope: "decidim.results.admin")
44
+ render action: "edit"
45
+ end
46
+ end
47
+ end
48
+
49
+ def destroy
50
+ result.destroy!
51
+
52
+ flash[:notice] = I18n.t("results.destroy.success", scope: "decidim.results.admin")
53
+
54
+ redirect_to results_path
55
+ end
56
+
57
+ private
58
+
59
+ def results
60
+ @results ||= Result.where(feature: current_feature)
61
+ end
62
+
63
+ def result
64
+ @result ||= results.find(params[:id])
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Results
5
+ # This controller is the abstract class from which all other controllers of
6
+ # this engine inherit.
7
+ #
8
+ # Note that it inherits from `Decidim::Components::BaseController`, which
9
+ # override its layout and provide all kinds of useful methods.
10
+ class ApplicationController < Decidim::Features::BaseController
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Results
5
+ # Exposes the result resource so users can view them
6
+ class ResultsController < Decidim::Results::ApplicationController
7
+ include FilterResource
8
+
9
+ helper_method :results, :result, :stats_calculator
10
+
11
+ private
12
+
13
+ def results
14
+ @results ||= search.results.page(params[:page]).per(12)
15
+ end
16
+
17
+ def result
18
+ @result ||= results.find(params[:id])
19
+ end
20
+
21
+ def stats_calculator
22
+ @stats_calculator ||= ResultStatsCalculator.new(result)
23
+ end
24
+
25
+ def search_klass
26
+ ResultSearch
27
+ end
28
+
29
+ def default_filter_params
30
+ {
31
+ search_text: "",
32
+ scope_id: "",
33
+ category_id: ""
34
+ }
35
+ end
36
+
37
+ def context_params
38
+ { feature: current_feature, organization: current_organization }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ module Admin
5
+ # This class holds a Form to create/update results from Decidim's admin panel.
6
+ class ResultForm < Decidim::Form
7
+ include TranslatableAttributes
8
+ include TranslationsHelper
9
+
10
+ translatable_attribute :title, String
11
+ translatable_attribute :description, String
12
+ attribute :decidim_scope_id, Integer
13
+ attribute :decidim_category_id, Integer
14
+ attribute :proposal_ids, Array[Integer]
15
+
16
+ validates :title, translatable_presence: true
17
+ validates :description, translatable_presence: true
18
+
19
+ validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
20
+ validates :category, presence: true, if: ->(form) { form.decidim_category_id.present? }
21
+
22
+ def map_model(model)
23
+ self.proposal_ids = model.linked_resources(:proposals, "included_proposals").pluck(:id)
24
+ end
25
+
26
+ def proposals
27
+ @proposals ||= Decidim.find_resource_manifest(:proposals).try(:resource_scope, context.current_feature)&.order(title: :asc)&.pluck(:title, :id)
28
+ end
29
+
30
+ def scope
31
+ @scope ||= context.current_feature.scopes.where(id: decidim_scope_id).first
32
+ end
33
+
34
+ def category
35
+ @category ||= context.current_feature.categories.where(id: decidim_category_id).first
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ # Custom helpers, scoped to the meetings engine.
5
+ #
6
+ module ApplicationHelper
7
+ include PaginateHelper
8
+ include Decidim::Comments::CommentsHelper
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ # Abstract class from which all models in this engine inherit.
5
+ class ApplicationRecord < ActiveRecord::Base
6
+ self.abstract_class = true
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ # The data store for a Result in the Decidim::Results component. It stores a
5
+ # title, description and any other useful information to render a custom result.
6
+ class Result < Results::ApplicationRecord
7
+ include Decidim::Resourceable
8
+ include Decidim::HasFeature
9
+ include Decidim::HasScope
10
+ include Decidim::HasCategory
11
+
12
+ feature_manifest_name "results"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ # This class handles search and filtering of results. Needs a
5
+ # `current_feature` param with a `Decidim::Feature` in order to
6
+ # find the results.
7
+ class ResultSearch < ResourceSearch
8
+ # Public: Initializes the service.
9
+ # feature - A Decidim::Feature to get the results from.
10
+ # page - The page number to paginate the results.
11
+ # per_page - The number of proposals to return per page.
12
+ def initialize(options = {})
13
+ super(Result.all, options)
14
+ end
15
+
16
+ # Handle the search_text filter
17
+ def search_search_text
18
+ query
19
+ .where(localized_search_text_in(:title), text: "%#{search_text}%")
20
+ .or(query.where(localized_search_text_in(:description), text: "%#{search_text}%"))
21
+ end
22
+
23
+ # Handle the scope_id filter
24
+ def search_scope_id
25
+ query.where(decidim_scope_id: scope_id)
26
+ end
27
+
28
+ private
29
+
30
+ # Internal: builds the needed query to search for a text in the organization's
31
+ # available locales. Note that it is intended to be used as follows:
32
+ #
33
+ # Example:
34
+ # Resource.where(localized_search_text_for(:title, text: "my_query"))
35
+ #
36
+ # The Hash with the `:text` key is required or it won't work.
37
+ def localized_search_text_in(field)
38
+ options[:organization].available_locales.map do |l|
39
+ "#{field} ->> '#{l}' ILIKE :text"
40
+ end.join(" OR ")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ # This class handles statistics of results. Needs a `result` in
5
+ # order to find the stats.
6
+ class ResultStatsCalculator
7
+ # Public: Initializes the service.
8
+ # result - The result from which to calculate the stats.
9
+ def initialize(result)
10
+ @result = result
11
+ end
12
+
13
+ def proposals_count
14
+ proposals.count
15
+ end
16
+
17
+ def votes_count
18
+ return 0 unless proposals
19
+ proposals.sum { |proposal| proposal.votes.size }
20
+ end
21
+
22
+ def comments_count
23
+ Decidim::Comments::Comment.where(commentable: proposals).count
24
+ end
25
+
26
+ def attendees_count
27
+ meetings.where("attendees_count > 0").sum(:attendees_count)
28
+ end
29
+
30
+ def contributions_count
31
+ meetings.where("contributions_count > 0").sum(:contributions_count)
32
+ end
33
+
34
+ def meetings_count
35
+ meetings.count
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :result
41
+
42
+ def proposals
43
+ @proposals ||= result.linked_resources(:proposals, "included_proposals")
44
+ end
45
+
46
+ def meetings
47
+ @meetings ||= result.linked_resources(:meetings, "meetings_through_proposals")
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,23 @@
1
+ <div class="field" >
2
+ <%= form.translated :text_field, :title, autofocus: true %>
3
+ </div>
4
+
5
+ <div class="field" >
6
+ <%= form.translated :editor, :description %>
7
+ </div>
8
+
9
+ <div class="field" >
10
+ <%= form.collection_select :decidim_scope_id, current_organization.scopes, :id, :name, include_blank: true %>
11
+ </div>
12
+
13
+ <div class="field" >
14
+ <%= form.categories_select :decidim_category_id, current_participatory_process.categories, include_blank: true, disable_parents: false %>
15
+ </div>
16
+
17
+ <% if @form.proposals %>
18
+ <%= form.select :proposal_ids,
19
+ @form.proposals,
20
+ {},
21
+ { multiple: true, class: "chosen-select" }
22
+ %>
23
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <h3><%= t ".title" %></h3>
2
+
3
+ <%= form_for(@form) do |f| %>
4
+ <%= render partial: 'form', object: f %>
5
+
6
+ <div class="actions">
7
+ <%= f.submit t(".update") %>
8
+ </div>
9
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <h2><%= t(".title") %></h2>
2
+
3
+ <div class="actions title">
4
+ <%= link_to t("actions.new", scope: "decidim.results", name: t("models.result.name", scope: "decidim.results.admin")), new_result_path, class: 'new' if can? :manage, current_feature %>
5
+ </div>
6
+
7
+ <table class="stack">
8
+ <thead>
9
+ <tr>
10
+ <th><%= t("models.result.fields.title", scope: "decidim.results") %></th>
11
+ <th class="actions"><%= t("actions.title", scope: "decidim.results") %></th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% results.each do |result| %>
16
+ <tr data-id="<%= result.id %>">
17
+ <td>
18
+ <%= link_to translated_attribute(result.title), decidim_results.result_path(id: result, feature_id: current_feature, participatory_process_id: current_participatory_process), target: :blank %><br />
19
+ </td>
20
+ <td class="actions">
21
+ <%= link_to t("actions.edit", scope: "decidim.results"), edit_result_path(result) if can? :update, current_feature %>
22
+ <%= link_to t("actions.destroy", scope: "decidim.results"), result_path(result), method: :delete, class: "small alert button", data: { confirm: t("actions.confirm_destroy", scope: "decidim.results") } if can? :destroy, current_feature %>
23
+ </td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+ </table>
@@ -0,0 +1,9 @@
1
+ <h3><%= t ".title" %></h3>
2
+
3
+ <%= form_for(@form) do |f| %>
4
+ <%= render partial: 'form', object: f %>
5
+
6
+ <div class="actions">
7
+ <%= f.submit t(".create") %>
8
+ </div>
9
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= t(".results_count", count: results.total_count) %>
@@ -0,0 +1,22 @@
1
+ <%= filter_form_for filter do |form| %>
2
+ <div class="filters__section">
3
+ <div class="filters__search">
4
+ <div class="input-group">
5
+ <%= form.search_field :search_text, label: false, class: "input-group-field", placeholder: t('.search') %>
6
+ <div class="input-group-button">
7
+ <button type="submit" class="button button--muted">
8
+ <%= icon "magnifying-glass", aria_label: t('.search') %>
9
+ </button>
10
+ </div>
11
+ </div>
12
+ </div>
13
+ </div>
14
+
15
+ <% if current_organization.scopes.any? %>
16
+ <%= form.collection_check_boxes :scope_id, current_organization.scopes, lambda {|scope| scope.id.to_s}, :name, legend_title: t('.scopes') %>
17
+ <% end %>
18
+
19
+ <% if current_feature.categories.any? %>
20
+ <%= form.categories_select :category_id, current_feature.categories, legend_title: t('.category'), disable_parents: false, label: false, include_blank: true %>
21
+ <% end %>
22
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <div class="card card--action card--list">
2
+ <% resources.each do |result| %>
3
+ <div class="card--list__item">
4
+ <%= icon "actions", class: "card--list__icon", remove_icon_class: true %>
5
+ <%= link_to decidim_resource_path(result), class: "card--list__text card__link card__link--block" do %>
6
+ <h5 class="card--list__heading">
7
+ <%= translated_attribute(result.title) %>
8
+ </h5>
9
+ <% end %>
10
+ </div>
11
+ <% end %>
12
+ </div>
@@ -0,0 +1,16 @@
1
+ <div class="row small-up-1 medium-up-2 card-grid">
2
+ <% results.each do |result| %>
3
+ <div class="column">
4
+ <article class="card card--result">
5
+ <div class="card__content">
6
+ <%= link_to result, class: "card__link" do %>
7
+ <h5 class="card__title"><%= translated_attribute result.title %></h5>
8
+ <% end %>
9
+ <%== translated_attribute result.description %>
10
+ <%= render partial: "tags", locals: { result: result } %>
11
+ </div>
12
+ </article>
13
+ </div>
14
+ <% end %>
15
+ </div>
16
+ <%= decidim_paginate results, order_start_time: params[:order_start_time], scope_id: params[:scope_id] %>
@@ -0,0 +1,10 @@
1
+ <% if result.category.present? || result.scope.present? %>
2
+ <ul class="tags tags--result" >
3
+ <% if result.category.present? %>
4
+ <li><%= link_to translated_attribute(result.category.name), decidim_results.results_path(filter: { category_id: result.category.id }) %></li>
5
+ <% end %>
6
+ <% if result.scope.present? %>
7
+ <li><%= link_to result.scope.name, decidim_results.results_path(filter: { scope_id: [result.scope.id] }) %></li>
8
+ <% end %>
9
+ </ul>
10
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <% content_for(:title, t(".title")) %>
2
+
3
+ <div class="row columns">
4
+ <div class="title-action">
5
+ <h2 id="proposals-count" class="title-action__title section-heading">
6
+ <%= render partial: "count" %>
7
+ </h2>
8
+ </div>
9
+ </div>
10
+
11
+ <div class="row">
12
+ <div class="columns mediumlarge-4 large-3">
13
+ <div class="card card--secondary show-for-mediumlarge" >
14
+ <%= render partial: "filters" %>
15
+ </div>
16
+ </div>
17
+ <div id="results" class="columns mediumlarge-8 large-9">
18
+ <%= render partial: "results" %>
19
+ </div>
20
+ </div>
21
+
22
+ <%= javascript_include_tag("decidim/filters") %>
@@ -0,0 +1,2 @@
1
+ var $results = $('#results');
2
+ $results.html('<%= j(render partial: "results") %>');
@@ -0,0 +1,52 @@
1
+ <% content_for(:title, translated_attribute(result.title)) %>
2
+
3
+ <div class="row column view-header">
4
+ <h2 class="heading2"><%= translated_attribute result.title %></h2>
5
+ </div>
6
+ <div class="row">
7
+ <div class="columns section view-side mediumlarge-4 mediumlarge-push-8
8
+ large-3 large-push-9">
9
+ <div class="card extra">
10
+ <div class="definition-data">
11
+ <div class="definition-data__item definition-data__item--double">
12
+ <span class="definition-data__title"><%= t(".stats.proposals") %></span>
13
+ <span class="definition-data__number"><%= stats_calculator.proposals_count %></span>
14
+ </div>
15
+ <div class="definition-data__item definition-data__item--double">
16
+ <span class="definition-data__title"><%= t(".stats.meetings") %></span>
17
+ <span class="definition-data__number"><%= stats_calculator.meetings_count %></span>
18
+ </div>
19
+ <div class="definition-data__item definition-data__item--double">
20
+ <span class="definition-data__title"><%= t(".stats.comments") %></span>
21
+ <span class="definition-data__number"><%= stats_calculator.comments_count %></span>
22
+ </div>
23
+ <div class="definition-data__item definition-data__item--double">
24
+ <span class="definition-data__title"><%= t(".stats.attendees") %></span>
25
+ <span class="definition-data__number"><%= stats_calculator.attendees_count %></span>
26
+ </div>
27
+ <div class="definition-data__item definition-data__item--double">
28
+ <span class="definition-data__title"><%= t(".stats.votes") %></span>
29
+ <span class="definition-data__number"><%= stats_calculator.votes_count %></span>
30
+ </div>
31
+ <div class="definition-data__item definition-data__item--double">
32
+ <span class="definition-data__title"><%= t(".stats.contributions") %></span>
33
+ <span class="definition-data__number"><%= stats_calculator.contributions_count %></span>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <div class="columns mediumlarge-8 mediumlarge-pull-4">
39
+ <div class="section">
40
+ <%== translated_attribute result.description %>
41
+ <%= render partial: "tags", locals: { result: result } %>
42
+ </div>
43
+ <%= linked_resources_for result, :proposals, "included_proposals" %>
44
+ <%= linked_resources_for result, :meetings, "meetings_through_proposals" %>
45
+ </div>
46
+ </div>
47
+
48
+ <%= content_for :expanded do %>
49
+ <% if feature_settings.comments_always_enabled || current_settings.comments_enabled %>
50
+ <%= comments_for result, arguable: true, votable: true %>
51
+ <% end %>
52
+ <% end %>
@@ -0,0 +1,5 @@
1
+ base_locale: en
2
+ locales: [en]
3
+ ignore_unused:
4
+ - "decidim.features.results.name"
5
+ - "decidim.resource_links.*"
@@ -0,0 +1,61 @@
1
+ ca:
2
+ decidim:
3
+ features:
4
+ results:
5
+ name: Resultats
6
+ resource_links:
7
+ included_proposals:
8
+ proposals: Propostes incloses en aquest resultat
9
+ meetings_through_proposals:
10
+ meetings: Trobades relacionades
11
+ results:
12
+ actions:
13
+ confirm_destroy: Esteu segur que voleu suprimir aquest resultat?
14
+ destroy: Esborra
15
+ edit: Edita
16
+ new: Nou/va %{name}
17
+ title: Accions
18
+ admin:
19
+ models:
20
+ result:
21
+ name: Resultat
22
+ results:
23
+ create:
24
+ invalid: Hi ha hagut un problema en crear aquest resultat
25
+ success: El resultat ha estat creat correctament
26
+ destroy:
27
+ success: El resultat ha estat eliminat correctament
28
+ edit:
29
+ title: Edita resultat
30
+ update: Actualitza resultat
31
+ index:
32
+ title: Resultats
33
+ new:
34
+ create: Crea resultat
35
+ title: Nou resultat
36
+ update:
37
+ invalid: Hi ha hagut un problema en actualitzar aquest resultat
38
+ success: El resultat ha estat actualitzat correctament
39
+ models:
40
+ result:
41
+ fields:
42
+ title: Títol
43
+ results:
44
+ count:
45
+ results_count:
46
+ one: 1 resultat
47
+ other: "%{count} resultats"
48
+ filters:
49
+ category: Categoria
50
+ scopes: Àmbits
51
+ search: Cerca
52
+ index:
53
+ title: Resultats
54
+ show:
55
+ stats:
56
+ attendees: Assistents
57
+ comments: Comentaris
58
+ contributions: Aportacions
59
+ meetings: Llistat de trobades
60
+ proposals: Propostes
61
+ votes: Suports
@@ -0,0 +1,62 @@
1
+ ---
2
+ en:
3
+ decidim:
4
+ features:
5
+ results:
6
+ name: Results
7
+ resource_links:
8
+ included_proposals:
9
+ proposals: Proposals included in this result
10
+ meetings_through_proposals:
11
+ meetings: Related meetings
12
+ results:
13
+ actions:
14
+ confirm_destroy: Are you sure you want to delete this result?
15
+ destroy: Delete
16
+ edit: Edit
17
+ new: New %{name}
18
+ title: Actions
19
+ admin:
20
+ models:
21
+ result:
22
+ name: Result
23
+ results:
24
+ create:
25
+ invalid: There's been a problem creating this result
26
+ success: Result successfully created
27
+ destroy:
28
+ success: Result successfully deleted
29
+ edit:
30
+ title: Edit result
31
+ update: Update result
32
+ index:
33
+ title: Results
34
+ new:
35
+ create: Create result
36
+ title: New result
37
+ update:
38
+ invalid: There's been a problem updating this result
39
+ success: Result successfully updated
40
+ models:
41
+ result:
42
+ fields:
43
+ title: Title
44
+ results:
45
+ count:
46
+ results_count:
47
+ one: 1 result
48
+ other: "%{count} results"
49
+ filters:
50
+ category: Category
51
+ scopes: Scopes
52
+ search: Search
53
+ index:
54
+ title: Results
55
+ show:
56
+ stats:
57
+ attendees: Attendees
58
+ comments: Comments
59
+ contributions: Contributions
60
+ meetings: Meetings
61
+ proposals: Proposals
62
+ votes: Supports
@@ -0,0 +1,61 @@
1
+ es:
2
+ decidim:
3
+ features:
4
+ results:
5
+ name: Resultados
6
+ resource_links:
7
+ included_proposals:
8
+ proposals: Propuestas incluidas en este resultado
9
+ meetings_through_proposals:
10
+ meetings: Encuentros relacionados
11
+ results:
12
+ actions:
13
+ confirm_destroy: '¿Está seguro de que quiere eliminar este resultado?'
14
+ destroy: Borrar
15
+ edit: Editar
16
+ new: Nuevo/a %{name}
17
+ title: Acciones
18
+ admin:
19
+ models:
20
+ result:
21
+ name: Resultado
22
+ results:
23
+ create:
24
+ invalid: Ha habido un problema al crear este resultado
25
+ success: El resultado ha sido creado correctamente
26
+ destroy:
27
+ success: El resultado se ha eliminado correctamente
28
+ edit:
29
+ title: Editar resultado
30
+ update: Actualizar resultado
31
+ index:
32
+ title: Resultados
33
+ new:
34
+ create: Crear resultado
35
+ title: Nuevo resultado
36
+ update:
37
+ invalid: Ha habido un problema al actualizar este resultado
38
+ success: El resultado ha actualizado correctamente
39
+ models:
40
+ result:
41
+ fields:
42
+ title: Título
43
+ results:
44
+ count:
45
+ results_count:
46
+ one: 1 resultado
47
+ other: "%{count} resultados"
48
+ filters:
49
+ category: Categoría
50
+ scopes: Ámbitos
51
+ search: Buscar
52
+ index:
53
+ title: Resultados
54
+ show:
55
+ stats:
56
+ attendees: Asistentes
57
+ comments: Comentarios
58
+ contributions: Aportes
59
+ meetings: Lista de encuentros
60
+ proposals: Propuestas
61
+ votes: Soportes
@@ -0,0 +1,14 @@
1
+ class CreateResults < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :decidim_results_results do |t|
4
+ t.jsonb :title
5
+ t.jsonb :description
6
+ t.jsonb :short_description
7
+ t.references :decidim_feature, index: true
8
+ t.references :decidim_scope, index: true
9
+ t.references :decidim_category, index: true
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ class RemoveShortDescriptionFromResults < ActiveRecord::Migration[5.0]
2
+ def change
3
+ remove_column :decidim_results_results, :short_description
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ # This module contains all the domain logic associated to Decidim's Results
5
+ # component admin panel.
6
+ module Admin
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Results
4
+ # This is the engine that runs on the public interface of `decidim-results`.
5
+ # It mostly handles rendering the created reuslts associated to a participatory
6
+ # process.
7
+ class AdminEngine < ::Rails::Engine
8
+ isolate_namespace Decidim::Results::Admin
9
+
10
+ paths["db/migrate"] = nil
11
+
12
+ routes do
13
+ resources :results
14
+ root to: "results#index"
15
+ end
16
+
17
+ def load_seed
18
+ nil
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "decidim/features/namer"
4
+
5
+ Decidim.register_feature(:results) do |feature|
6
+ feature.engine = Decidim::Results::ListEngine
7
+ feature.admin_engine = Decidim::Results::AdminEngine
8
+ feature.icon = "decidim/results/icon.svg"
9
+
10
+ feature.on(:before_destroy) do |instance|
11
+ raise StandardError, "Can't remove this feature" if Decidim::Results::Result.where(feature: instance).any?
12
+ end
13
+
14
+ feature.register_resource do |resource|
15
+ resource.model_class_name = "Decidim::Results::Result"
16
+ resource.template = "decidim/results/results/linked_results"
17
+ end
18
+
19
+ feature.settings(:global) do |settings|
20
+ settings.attribute :comments_always_enabled, type: :boolean, default: true
21
+ end
22
+
23
+ feature.settings(:step) do |settings|
24
+ settings.attribute :comments_enabled, type: :boolean, default: true
25
+ end
26
+
27
+ feature.seeds do
28
+ Decidim::ParticipatoryProcess.all.each do |process|
29
+ next unless process.steps.any?
30
+
31
+ feature = Decidim::Feature.create!(
32
+ name: Decidim::Features::Namer.new(process.organization.available_locales, :results).i18n_name,
33
+ manifest_name: :results,
34
+ participatory_process: process
35
+ )
36
+
37
+ 3.times do
38
+ result = Decidim::Results::Result.create!(
39
+ feature: feature,
40
+ scope: process.organization.scopes.sample,
41
+ category: process.categories.sample,
42
+ title: Decidim::Faker::Localized.sentence(2),
43
+ description: Decidim::Faker::Localized.wrapped("<p>", "</p>") do
44
+ Decidim::Faker::Localized.paragraph(3)
45
+ end
46
+ )
47
+
48
+ Decidim::Comments::Seed.comments_for(result)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ require "searchlight"
3
+ require "kaminari"
4
+
5
+ module Decidim
6
+ module Results
7
+ # This is the engine that runs on the public interface of `decidim-results`.
8
+ # It mostly handles rendering the created results associated to a participatory
9
+ # process.
10
+ class ListEngine < ::Rails::Engine
11
+ isolate_namespace Decidim::Results
12
+
13
+ routes do
14
+ resources :results, only: [:index, :show]
15
+ root to: "results#index"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require "decidim/faker/localized"
3
+ require "decidim/dev"
4
+
5
+ FactoryGirl.define do
6
+ factory :result, class: Decidim::Results::Result do
7
+ title { Decidim::Faker::Localized.sentence(3) }
8
+ description { Decidim::Faker::Localized.wrapped("<p>", "</p>") { Decidim::Faker::Localized.sentence(4) } }
9
+ feature { build(:feature, manifest_name: "results") }
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require "decidim/results/admin"
3
+ require "decidim/results/list_engine"
4
+ require "decidim/results/admin_engine"
5
+ require "decidim/results/feature"
6
+
7
+ module Decidim
8
+ # Base module for this engine.
9
+ module Results
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,213 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: decidim-results
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Josep Jaume Rey Peroy
8
+ - Marc Riera Casals
9
+ - Oriol Gual Oliva
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2017-02-02 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: decidim-core
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.0.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - '='
27
+ - !ruby/object:Gem::Version
28
+ version: 0.0.3
29
+ - !ruby/object:Gem::Dependency
30
+ name: decidim-comments
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - '='
34
+ - !ruby/object:Gem::Version
35
+ version: 0.0.3
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '='
41
+ - !ruby/object:Gem::Version
42
+ version: 0.0.3
43
+ - !ruby/object:Gem::Dependency
44
+ name: rectify
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.8'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '0.8'
57
+ - !ruby/object:Gem::Dependency
58
+ name: searchlight
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 4.1.0
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: 4.1.0
71
+ - !ruby/object:Gem::Dependency
72
+ name: kaminari
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: 1.0.0.rc1
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: 1.0.0.rc1
85
+ - !ruby/object:Gem::Dependency
86
+ name: decidim-dev
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '='
90
+ - !ruby/object:Gem::Version
91
+ version: 0.0.3
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '='
97
+ - !ruby/object:Gem::Version
98
+ version: 0.0.3
99
+ - !ruby/object:Gem::Dependency
100
+ name: decidim-comments
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '='
104
+ - !ruby/object:Gem::Version
105
+ version: 0.0.3
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - '='
111
+ - !ruby/object:Gem::Version
112
+ version: 0.0.3
113
+ - !ruby/object:Gem::Dependency
114
+ name: decidim-meetings
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - '='
118
+ - !ruby/object:Gem::Version
119
+ version: 0.0.3
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - '='
125
+ - !ruby/object:Gem::Version
126
+ version: 0.0.3
127
+ - !ruby/object:Gem::Dependency
128
+ name: decidim-proposals
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '='
132
+ - !ruby/object:Gem::Version
133
+ version: 0.0.3
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - '='
139
+ - !ruby/object:Gem::Version
140
+ version: 0.0.3
141
+ description: ''
142
+ email:
143
+ - josepjaume@gmail.com
144
+ - mrc2407@gmail.com
145
+ - oriolgual@gmail.com
146
+ executables: []
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - README.md
151
+ - Rakefile
152
+ - app/assets/images/decidim/results/icon.svg
153
+ - app/commands/decidim/results/admin/create_result.rb
154
+ - app/commands/decidim/results/admin/update_result.rb
155
+ - app/controllers/decidim/results/admin/application_controller.rb
156
+ - app/controllers/decidim/results/admin/results_controller.rb
157
+ - app/controllers/decidim/results/application_controller.rb
158
+ - app/controllers/decidim/results/results_controller.rb
159
+ - app/forms/decidim/results/admin/result_form.rb
160
+ - app/helpers/decidim/results/application_helper.rb
161
+ - app/models/decidim/results/application_record.rb
162
+ - app/models/decidim/results/result.rb
163
+ - app/services/decidim/results/result_search.rb
164
+ - app/services/decidim/results/result_stats_calculator.rb
165
+ - app/views/decidim/results/admin/results/_form.html.erb
166
+ - app/views/decidim/results/admin/results/edit.html.erb
167
+ - app/views/decidim/results/admin/results/index.html.erb
168
+ - app/views/decidim/results/admin/results/new.html.erb
169
+ - app/views/decidim/results/results/_count.html.erb
170
+ - app/views/decidim/results/results/_filters.html.erb
171
+ - app/views/decidim/results/results/_linked_results.html.erb
172
+ - app/views/decidim/results/results/_results.html.erb
173
+ - app/views/decidim/results/results/_tags.html.erb
174
+ - app/views/decidim/results/results/index.html.erb
175
+ - app/views/decidim/results/results/index.js.erb
176
+ - app/views/decidim/results/results/show.html.erb
177
+ - config/i18n-tasks.yml
178
+ - config/locales/ca.yml
179
+ - config/locales/en.yml
180
+ - config/locales/es.yml
181
+ - db/migrate/20170116104125_create_results.rb
182
+ - db/migrate/20170129164553_remove_short_description_from_results.rb
183
+ - lib/decidim/results.rb
184
+ - lib/decidim/results/admin.rb
185
+ - lib/decidim/results/admin_engine.rb
186
+ - lib/decidim/results/feature.rb
187
+ - lib/decidim/results/list_engine.rb
188
+ - lib/decidim/results/test/factories.rb
189
+ homepage: https://github.com/AjuntamentdeBarcelona/decidim
190
+ licenses:
191
+ - AGPLv3
192
+ metadata: {}
193
+ post_install_message:
194
+ rdoc_options: []
195
+ require_paths:
196
+ - lib
197
+ required_ruby_version: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: 2.3.1
202
+ required_rubygems_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ requirements: []
208
+ rubyforge_project:
209
+ rubygems_version: 2.6.8
210
+ signing_key:
211
+ specification_version: 4
212
+ summary: A results component for decidim's participatory processes.
213
+ test_files: []