decidim-comparative_stats 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +187 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/config/admin/comparative_stats_manifest.css +4 -0
  6. data/app/assets/config/admin/comparative_stats_manifest.js +2 -0
  7. data/app/assets/config/comparative_stats_manifest.css +4 -0
  8. data/app/assets/config/comparative_stats_manifest.js +2 -0
  9. data/app/assets/images/bcn-logo.png +0 -0
  10. data/app/assets/images/decidim/comparative_stats/icon.svg +1 -0
  11. data/app/assets/images/platoniq-logo.png +0 -0
  12. data/app/assets/javascripts/decidim/comparative_stats/geocoded_events.js.es6 +94 -0
  13. data/app/assets/javascripts/decidim/comparative_stats/graphs.js.es6 +13 -0
  14. data/app/assets/stylesheets/decidim/comparative_stats/geocoded_events.scss +23 -0
  15. data/app/assets/stylesheets/decidim/comparative_stats/graphs.scss +6 -0
  16. data/app/assets/stylesheets/decidim/comparative_stats/widget.scss +8 -0
  17. data/app/cells/decidim/comparative_stats/metric_piecharts/show.erb +14 -0
  18. data/app/cells/decidim/comparative_stats/metric_piecharts_cell.rb +39 -0
  19. data/app/cells/decidim/comparative_stats/metric_timelines/show.erb +14 -0
  20. data/app/cells/decidim/comparative_stats/metric_timelines_cell.rb +23 -0
  21. data/app/cells/decidim/comparative_stats/participatory_processes_timeline/show.erb +55 -0
  22. data/app/cells/decidim/comparative_stats/participatory_processes_timeline_cell.rb +51 -0
  23. data/app/cells/decidim/comparative_stats/participatory_spaces_geocoded_events/show.erb +55 -0
  24. data/app/cells/decidim/comparative_stats/participatory_spaces_geocoded_events_cell.rb +108 -0
  25. data/app/commands/decidim/comparative_stats/admin/create_endpoint.rb +46 -0
  26. data/app/commands/decidim/comparative_stats/admin/destroy_endpoint.rb +43 -0
  27. data/app/commands/decidim/comparative_stats/admin/update_endpoint.rb +47 -0
  28. data/app/controllers/decidim/comparative_stats/admin/application_controller.rb +16 -0
  29. data/app/controllers/decidim/comparative_stats/admin/endpoints_controller.rb +83 -0
  30. data/app/controllers/decidim/comparative_stats/admin/graphs_controller.rb +16 -0
  31. data/app/controllers/decidim/comparative_stats/widgets_controller.rb +31 -0
  32. data/app/forms/decidim/comparative_stats/admin/endpoint_form.rb +26 -0
  33. data/app/helpers/decidim/comparative_stats/application_helper.rb +27 -0
  34. data/app/models/decidim/comparative_stats/application_record.rb +10 -0
  35. data/app/models/decidim/comparative_stats/endpoint.rb +22 -0
  36. data/app/permissions/decidim/comparative_stats/admin/permissions.rb +24 -0
  37. data/app/presenters/decidim/comparative_stats/admin_log/endpoint_presenter.rb +46 -0
  38. data/app/views/decidim/comparative_stats/admin/endpoints/_form.html.erb +7 -0
  39. data/app/views/decidim/comparative_stats/admin/endpoints/edit.html.erb +16 -0
  40. data/app/views/decidim/comparative_stats/admin/endpoints/index.html.erb +66 -0
  41. data/app/views/decidim/comparative_stats/admin/endpoints/new.html.erb +16 -0
  42. data/app/views/decidim/comparative_stats/admin/graphs/show.html.erb +15 -0
  43. data/app/views/decidim/comparative_stats/widgets/_all.html.erb +1 -0
  44. data/app/views/decidim/comparative_stats/widgets/_embed.html.erb +7 -0
  45. data/app/views/decidim/comparative_stats/widgets/_embed_modal.html.erb +27 -0
  46. data/app/views/decidim/comparative_stats/widgets/_global_stats.html.erb +4 -0
  47. data/app/views/decidim/comparative_stats/widgets/_global_stats_timeline.html.erb +4 -0
  48. data/app/views/decidim/comparative_stats/widgets/_processes_timeline.html.erb +4 -0
  49. data/app/views/decidim/comparative_stats/widgets/_spaces_geocoded_events.html.erb +5 -0
  50. data/app/views/decidim/comparative_stats/widgets/_tabs.html.erb +19 -0
  51. data/app/views/decidim/comparative_stats/widgets/show.html.erb +1 -0
  52. data/app/views/layouts/decidim/admin/comparative_stats.html.erb +28 -0
  53. data/app/views/layouts/decidim/comparative_stats/widget.html.erb +22 -0
  54. data/config/i18n-tasks.yml +10 -0
  55. data/config/locales/ca.yml +57 -0
  56. data/config/locales/cs.yml +57 -0
  57. data/config/locales/en.yml +64 -0
  58. data/config/locales/es.yml +57 -0
  59. data/db/migrate/20191219104548_create_decidim_comparative_stats_endpoints.rb +13 -0
  60. data/db/migrate/20200122072955_add_name_version_to_comparative_stats_endpoints.rb +8 -0
  61. data/db/migrate/20200130203914_rename_version_field_in_comparative_stats_endpoints.rb +7 -0
  62. data/db/seeds.rb +42 -0
  63. data/lib/decidim/comparative_stats.rb +22 -0
  64. data/lib/decidim/comparative_stats/admin.rb +10 -0
  65. data/lib/decidim/comparative_stats/admin_engine.rb +50 -0
  66. data/lib/decidim/comparative_stats/api_fetcher.rb +93 -0
  67. data/lib/decidim/comparative_stats/cached_http_adapter.rb +23 -0
  68. data/lib/decidim/comparative_stats/engine.rb +34 -0
  69. data/lib/decidim/comparative_stats/queries/global_events.graphql +84 -0
  70. data/lib/decidim/comparative_stats/queries/global_history_metrics.graphql +10 -0
  71. data/lib/decidim/comparative_stats/queries/global_metrics.graphql +6 -0
  72. data/lib/decidim/comparative_stats/queries/name_and_version.graphql +6 -0
  73. data/lib/decidim/comparative_stats/queries/participatory_processes.graphql +12 -0
  74. data/lib/decidim/comparative_stats/queries/v022/global_events.graphql +76 -0
  75. data/lib/decidim/comparative_stats/test/factories.rb +13 -0
  76. data/lib/decidim/comparative_stats/version.rb +10 -0
  77. data/vendor/assets/images/draw/layers-2x.png +0 -0
  78. data/vendor/assets/images/draw/layers.png +0 -0
  79. data/vendor/assets/images/draw/marker-icon-2x.png +0 -0
  80. data/vendor/assets/images/draw/marker-icon.png +0 -0
  81. data/vendor/assets/images/draw/marker-shadow.png +0 -0
  82. data/vendor/assets/images/draw/spritesheet-2x.png +0 -0
  83. data/vendor/assets/images/draw/spritesheet.png +0 -0
  84. data/vendor/assets/images/draw/spritesheet.svg +156 -0
  85. data/vendor/assets/images/images/layers-2x.png +0 -0
  86. data/vendor/assets/images/images/layers.png +0 -0
  87. data/vendor/assets/images/images/marker-icon-2x.png +0 -0
  88. data/vendor/assets/images/images/marker-icon.png +0 -0
  89. data/vendor/assets/images/images/marker-shadow.png +0 -0
  90. data/vendor/assets/javascripts/leaflet.js +5 -0
  91. metadata +234 -0
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ module Admin
6
+ # This controller is the abstract class from which all other controllers of
7
+ # this engine inherit.
8
+ #
9
+ class ApplicationController < Decidim::Admin::ApplicationController
10
+ def permission_class_chain
11
+ [::Decidim::ComparativeStats::Admin::Permissions] + super
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ module Admin
6
+ class EndpointsController < ComparativeStats::Admin::ApplicationController
7
+ helper_method :organization_endpoints
8
+ layout "decidim/admin/comparative_stats"
9
+
10
+ def index
11
+ enforce_permission_to :index, :endpoint
12
+ end
13
+
14
+ def new
15
+ enforce_permission_to :create, :endpoint
16
+ @form = form(EndpointForm).instance
17
+ end
18
+
19
+ def edit
20
+ enforce_permission_to :update, :endpoint, endpoint: current_endpoint
21
+ @form = form(EndpointForm).from_model(current_endpoint, endpoint: current_endpoint)
22
+ end
23
+
24
+ def create
25
+ enforce_permission_to :create, :endpoint
26
+ @form = form(EndpointForm).from_params(params, api: api)
27
+ CreateEndpoint.call(@form) do
28
+ on(:ok) do
29
+ flash[:notice] = I18n.t("endpoints.create.success", scope: "decidim.comparative_stats.admin")
30
+ redirect_to decidim_admin_comparative_stats.endpoints_path
31
+ end
32
+
33
+ on(:invalid) do
34
+ flash.now[:alert] = I18n.t("endpoints.create.error", scope: "decidim.comparative_stats.admin")
35
+ render :new
36
+ end
37
+ end
38
+ end
39
+
40
+ def update
41
+ enforce_permission_to :update, :endpoint, endpoint: current_endpoint
42
+
43
+ form = form(EndpointForm).from_params(params, endpoint: current_endpoint, api: api)
44
+
45
+ UpdateEndpoint.call(current_endpoint, form, current_user) do
46
+ on(:ok) do
47
+ flash[:notice] = I18n.t("endpoints.update.success", scope: "decidim.comparative_stats.admin")
48
+ redirect_to decidim_admin_comparative_stats.endpoints_path
49
+ end
50
+
51
+ on(:invalid) do
52
+ flash.now[:alert] = I18n.t("endpoints.update.error", scope: "decidim.comparative_stats.admin")
53
+ render :edit
54
+ end
55
+ end
56
+ end
57
+
58
+ def destroy
59
+ enforce_permission_to :destroy, :endpoint, endpoint: current_endpoint
60
+
61
+ DestroyEndpoint.call(current_endpoint, current_user) do
62
+ on(:ok) do
63
+ flash[:notice] = I18n.t("endpoints.destroy.success", scope: "decidim.comparative_stats.admin")
64
+ redirect_to decidim_admin_comparative_stats.endpoints_path
65
+ end
66
+ end
67
+ end
68
+
69
+ def organization_endpoints
70
+ @organization_endpoints ||= Endpoint.where(organization: current_organization)
71
+ end
72
+
73
+ def api
74
+ @api ||= ApiFetcher.new(params[:endpoint][:endpoint])
75
+ end
76
+
77
+ def current_endpoint
78
+ @current_endpoint ||= Endpoint.find(params[:id])
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ module Admin
6
+ class GraphsController < ComparativeStats::Admin::ApplicationController
7
+ helper ComparativeStats::ApplicationHelper
8
+ layout "decidim/admin/comparative_stats"
9
+
10
+ def show
11
+ enforce_permission_to :show, :graph
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ class WidgetsController < Decidim::ApplicationController
6
+ helper ComparativeStats::ApplicationHelper
7
+ skip_before_action :verify_authenticity_token
8
+ after_action :allow_iframe, only: :show
9
+
10
+ helper_method :graphs_path
11
+
12
+ layout "decidim/comparative_stats/widget"
13
+
14
+ def show
15
+ is_valid = lookup_context.exists?(params[:graph], "decidim/comparative_stats/widgets", true)
16
+ render plain: "not found", status: :not_found unless is_valid
17
+ end
18
+
19
+ def allow_iframe
20
+ response.headers.delete "X-Frame-Options"
21
+ end
22
+
23
+ private
24
+
25
+ # Mock admin path for tabs.html.erb
26
+ def graphs_path(graph)
27
+ decidim_comparative_stats.widget_url graph
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ module Admin
6
+ # A form object used to configure the endpoint.
7
+ #
8
+ class EndpointForm < Decidim::Form
9
+ mimic :endpoint
10
+
11
+ attribute :endpoint, String, presence: true
12
+ attribute :active, Boolean
13
+ attribute :name, String
14
+
15
+ validate :valid_api_version
16
+
17
+ def valid_api_version
18
+ unless context.api.valid?
19
+ errors.add :endpoint, :invalid
20
+ errors.add :endpoint, context.api.error if context.api.error
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ # Custom helpers, scoped to the comparative_stats engine.
6
+ #
7
+ module ApplicationHelper
8
+ def embed_modal_for(url)
9
+ embed_code = String.new(content_tag(:iframe, "", src: url, frameborder: 0, width: "100%", height: "420", scrolling: "vertical"))
10
+ render partial: "decidim/comparative_stats/widgets/embed_modal", locals: { url: url, embed_code: embed_code }
11
+ end
12
+
13
+ def active_endpoints
14
+ Endpoint.active.where(organization: current_organization)
15
+ end
16
+
17
+ def available_graphs
18
+ [
19
+ :global_stats,
20
+ :global_stats_timeline,
21
+ :processes_timeline,
22
+ :spaces_geocoded_events
23
+ ]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ # Abstract class from which all models in this engine inherit.
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ self.abstract_class = true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim::ComparativeStats
4
+ class Endpoint < ApplicationRecord
5
+ include Decidim::Traceable
6
+ include Decidim::Loggable
7
+
8
+ self.table_name = "decidim_comparative_stats_endpoints"
9
+
10
+ belongs_to :organization, foreign_key: :decidim_organization_id, class_name: "Decidim::Organization"
11
+
12
+ scope :active, -> { where(active: true) }
13
+
14
+ def api
15
+ @api ||= ApiFetcher.new endpoint
16
+ end
17
+
18
+ def self.log_presenter_class_for(_log)
19
+ Decidim::ComparativeStats::AdminLog::EndpointPresenter
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ module Admin
6
+ class Permissions < Decidim::DefaultPermissions
7
+ def permissions
8
+ return permission_action unless user
9
+
10
+ return permission_action if permission_action.scope != :admin
11
+
12
+ return permission_action unless permission_action.subject.in? [:endpoint, :graph, :aggregated_maps]
13
+
14
+ case permission_action.action
15
+ when :index, :show, :create, :update, :destroy
16
+ permission_action.allow!
17
+ end
18
+
19
+ permission_action
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module ComparativeStats
5
+ module AdminLog
6
+ # This class holds the logic to present a `Decidim::ComparativeStats::Endpoint`
7
+ # for the `AdminLog` log.
8
+ #
9
+ # Usage should be automatic and you shouldn't need to call this class
10
+ # directly, but here's an example:
11
+ #
12
+ # action_log = Decidim::ActionLog.last
13
+ # view_helpers # => this comes from the views
14
+ # EndpointPresenter.new(action_log, view_helpers).present
15
+ class EndpointPresenter < Decidim::Log::BasePresenter
16
+ private
17
+
18
+ def diff_fields_mapping
19
+ {
20
+ endpoint: :string,
21
+ name: :string,
22
+ api_version: :string,
23
+ active: :boolean
24
+ }
25
+ end
26
+
27
+ def i18n_labels_scope
28
+ "activemodel.attributes.comparative_stats.endpoint"
29
+ end
30
+
31
+ def action_string
32
+ case action
33
+ when "create", "delete", "update"
34
+ "decidim.comparative_stats.admin_log.endpoint.#{action}"
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ def has_diff?
41
+ action == "delete" || super
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ <div class="row column">
2
+ <%= form.text_field :endpoint, autofocus: true %>
3
+ </div>
4
+
5
+ <div class="row column">
6
+ <%= form.check_box :active %>
7
+ </div>
@@ -0,0 +1,16 @@
1
+ <%= decidim_form_for(@form) do |f| %>
2
+ <div class="card">
3
+ <div class="card-divider">
4
+ <h2 class="card-title"><%= t ".title" %></h2>
5
+ </div>
6
+ <div class="card-section">
7
+ <%= render partial: "form", object: f %>
8
+ <div class="row column">
9
+ <%= f.text_field :name %>
10
+ </div>
11
+ </div>
12
+ </div>
13
+ <div class="button--double form-general-submit">
14
+ <%= f.submit t(".update") %>
15
+ </div>
16
+ <% end %>
@@ -0,0 +1,66 @@
1
+ <div class="card" id="translation_sets">
2
+ <div class="card-divider">
3
+ <h2 class="card-title flex--sbc">
4
+ <div>
5
+ <%= t ".title" %>
6
+ </div>
7
+
8
+ <div class="flex--cc flex-gap--1">
9
+ <div id="js-other-actions-wrapper">
10
+ <% if allowed_to? :create, :endpoint %>
11
+ <%= link_to t("endpoints.add", scope: "decidim.comparative_stats"), url_for(action: :new), class: "button tiny button--title new" %>
12
+ <% end %>
13
+ </div>
14
+ </div>
15
+ </h2>
16
+ </div>
17
+
18
+ <div class="card-section">
19
+ <div class="table-scroll">
20
+ <% if organization_endpoints.any? %>
21
+ <table class="table-list">
22
+ <thead>
23
+ <tr>
24
+ <th><%= t ".active" %></th>
25
+ <th><%= t ".endpoint" %></th>
26
+ <th><%= t ".name" %></th>
27
+ <th><%= t ".version" %></th>
28
+ <th></th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ <% organization_endpoints.each do |endpoint| %>
33
+ <tr data-id="<%= endpoint.id %>">
34
+ <td>
35
+ <% if endpoint.active? %>
36
+ <span class="icon-active"></span>
37
+ <% end %>
38
+ </td>
39
+ <td>
40
+ <%= endpoint.endpoint %><br>
41
+ </td>
42
+ <td>
43
+ <%= endpoint.name %><br>
44
+ </td>
45
+ <td>
46
+ <%= endpoint.api_version %><br>
47
+ </td>
48
+ <td class="table-list__actions">
49
+ <% if allowed_to? :update, :endpoint, endpoint: endpoint %>
50
+ <%= icon_link_to "pencil", edit_endpoint_path(endpoint), t("actions.edit", scope: "decidim.admin"), class: "action-icon--edit" %>
51
+ <% end %>
52
+
53
+ <% if allowed_to? :destroy, :endpoint, endpoint: endpoint %>
54
+ <%= icon_link_to "circle-x", endpoint_path(endpoint), t("actions.destroy", scope: "decidim.admin"), class: "action-icon--remove", method: :delete, data: { confirm: t("actions.confirm_destroy", scope: "decidim.admin") } %>
55
+ <% end %>
56
+ </td>
57
+ </tr>
58
+ <% end %>
59
+ </tbody>
60
+ </table>
61
+ <% else %>
62
+ <%= t ".no_endpoints" %>
63
+ <% end %>
64
+ </div>
65
+ </div>
66
+ </div>
@@ -0,0 +1,16 @@
1
+ <%= decidim_form_for(@form) do |f| %>
2
+ <div class="card">
3
+ <div class="card-divider">
4
+ <h2 class="card-title"><%= t ".title" %></h2>
5
+ </div>
6
+ <div class="card-section">
7
+ <p>
8
+ <%= t(".subtitle") %>
9
+ </p>
10
+ <%= render partial: "form", object: f %>
11
+ </div>
12
+ </div>
13
+ <div class="button--double form-general-submit">
14
+ <%= f.submit t(".create") %>
15
+ </div>
16
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <div class="card" id="translation_sets">
2
+ <div class="card-divider">
3
+ <h2 class="card-title flex--sbc">
4
+ <div>
5
+ <%= t ".title" %>
6
+ </div>
7
+ </h2>
8
+ </div>
9
+ </div>
10
+
11
+ <% if active_endpoints.any? %>
12
+ <%= render partial: "decidim/comparative_stats/widgets/tabs" %>
13
+ <% else %>
14
+ <%= t ".no_endpoints" %>
15
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render partial: "tabs" %>