decidim-comparative_stats 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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" %>