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,187 @@
1
+ # Decidim::ComparativeStats
2
+
3
+ ![Test](https://github.com/Platoniq/decidim-module-comparative_stats/workflows/Test/badge.svg)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/59d3415b89244ef0d40b/maintainability)](https://codeclimate.com/github/Platoniq/decidim-module-comparative_stats/maintainability)
5
+ [![Codecov](https://codecov.io/gh/Platoniq/decidim-module-comparative_stats/branch/master/graph/badge.svg?token=XhzWO0IiWt)](https://codecov.io/gh/Platoniq/decidim-module-comparative_stats)
6
+
7
+ Allows to compare different Decidim sites by accessing their GraphQL API and
8
+ generate graphs.
9
+
10
+ > NOTE: This module is in beta status, feel free to report issues or contribute in it!
11
+
12
+ ## Usage
13
+
14
+ This module works by connecting to several Decidim API's.
15
+ Just go to your `your-decidim-installation.org/admin/comparative_stats` and
16
+ add endpoints to compare.
17
+ For instance, Decidim Barcelona endpoint is `https://www.decidim.barcelona/api`
18
+
19
+ Then, go to graphs and see some nice graphs!
20
+
21
+ Graphs can be embedded anywhere independently, just use the button
22
+ `embed <>` and copy the code to any other place.
23
+
24
+ The generated code will look something like:
25
+
26
+ ```html
27
+ <iframe
28
+ src="http://your_url/comparative_stats/widgets/global_stats.html"
29
+ frameborder="0"
30
+ width="100%"
31
+ height="420"
32
+ scrolling="vertical"></iframe>
33
+ ```
34
+
35
+ You might want to customize the `height` parameter to match your particular case.
36
+
37
+ ![Barcelona/Helsinki comparison](example.png)
38
+ *A real example comparing the participatory processes carried out by the cities of Barcelona and Helsinki in 2019*
39
+
40
+ ## Installation
41
+
42
+ Add this line to your application's Gemfile:
43
+
44
+ ```ruby
45
+ gem "decidim-comparative_stats", "~> 1.0.1"
46
+ ```
47
+
48
+ And then execute:
49
+
50
+ ```bash
51
+ bundle
52
+ bundle exec rails decidim_comparative_stats:install:migrations
53
+ bundle exec rails db:migrate
54
+ ```
55
+
56
+ Depending on your Decidim version, choose the corresponding Awesome version to ensure
57
+ compatibility:
58
+
59
+ | Comparative stats version | Compatible Decidim versions |
60
+ |---|---|
61
+ | 1.0.x | 0.21.x - 0.23.x |
62
+
63
+ ## Configuration options
64
+
65
+ API queries are cached 1 hour by default.
66
+ This can be tuned by creating an initializer in your Decidim installation.
67
+ For instance, create the file `config/initializers/comparative_stats.rb`
68
+ with this content:
69
+
70
+ ```ruby
71
+ # frozen_string_literal: true
72
+
73
+ Decidim::ComparativeStats.configure do |config|
74
+ config.stats_cache_expiration_time = 2.hour
75
+ end
76
+ ```
77
+
78
+ ## Contributing
79
+
80
+ See [Decidim](https://github.com/decidim/decidim).
81
+
82
+ ### Developing
83
+
84
+ To start contributing to this project, first:
85
+
86
+ - Install the basic dependencies (such as Ruby and PostgreSQL)
87
+ - Clone this repository
88
+
89
+ Decidim's main repository also provides a Docker configuration file if you
90
+ prefer to use Docker instead of installing the dependencies locally on your
91
+ machine.
92
+
93
+ You can create the development app by running the following commands after
94
+ cloning this project:
95
+
96
+ ```bash
97
+ bundle
98
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake development_app
99
+ ```
100
+
101
+ Note that the database user has to have rights to create and drop a database in
102
+ order to create the dummy test app database.
103
+
104
+ Then to test how the module works in Decidim, start the development server:
105
+
106
+ ```bash
107
+ cd development_app
108
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rails s
109
+ ```
110
+
111
+ In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
112
+ [rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
113
+ can add the environment variables to the root directory of the project in a file
114
+ named `.rbenv-vars`. If these are defined for the environment, you can omit
115
+ defining these in the commands shown above.
116
+
117
+ #### Code Styling
118
+
119
+ Please follow the code styling defined by the different linters that ensure we
120
+ are all talking with the same language collaborating on the same project. This
121
+ project is set to follow the same rules that Decidim itself follows.
122
+
123
+ [Rubocop](https://rubocop.readthedocs.io/) linter is used for the Ruby language.
124
+
125
+ You can run the code styling checks by running the following commands from the
126
+ console:
127
+
128
+ ```
129
+ bundle exec rubocop
130
+ ```
131
+
132
+ To ease up following the style guide, you should install the plugin to your
133
+ favorite editor, such as:
134
+
135
+ - Atom - [linter-rubocop](https://atom.io/packages/linter-rubocop)
136
+ - Sublime Text - [Sublime RuboCop](https://github.com/pderichs/sublime_rubocop)
137
+ - Visual Studio Code - [Rubocop for Visual Studio Code](https://github.com/misogi/vscode-ruby-rubocop)
138
+
139
+ ### Testing
140
+
141
+ To run the tests run the following in the gem development path:
142
+
143
+ ```bash
144
+ bundle
145
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rake test_app
146
+ DATABASE_USERNAME=<username> DATABASE_PASSWORD=<password> bundle exec rspec
147
+ ```
148
+
149
+ Note that the database user has to have rights to create and drop a database in
150
+ order to create the dummy test app database.
151
+
152
+ In case you are using [rbenv](https://github.com/rbenv/rbenv) and have the
153
+ [rbenv-vars](https://github.com/rbenv/rbenv-vars) plugin installed for it, you
154
+ can add these environment variables to the root directory of the project in a
155
+ file named `.rbenv-vars`. In this case, you can omit defining these in the
156
+ commands shown above.
157
+
158
+ ### Test code coverage
159
+
160
+ If you want to generate the code coverage report for the tests, you can use
161
+ the `SIMPLECOV=1` environment variable in the rspec command as follows:
162
+
163
+ ```bash
164
+ SIMPLECOV=1 bundle exec rspec
165
+ ```
166
+
167
+ This will generate a folder named `coverage` in the project root which contains
168
+ the code coverage report.
169
+
170
+ ### Localization
171
+
172
+ If you would like to see this module in your own language, you can help with its
173
+ translation at Crowdin:
174
+
175
+ <https://crowdin.com/translate/decidim-comparative-stats>
176
+
177
+ ## License
178
+
179
+ This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
180
+
181
+ ## Credits
182
+
183
+ This plugin has been developed by ![Platoniq](app/assets/images/platoniq-logo.png)
184
+
185
+ > Amb el suport de l'Ajuntament de Barcelona - Direcció de Serveis d'Innovació Democràtica
186
+ >
187
+ > ![Logo Barcelona](app/assets/images/bcn-logo.png)
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
4
+
5
+ def install_module(path)
6
+ Dir.chdir(path) do
7
+ system("bundle exec rake decidim_comparative_stats:install:migrations")
8
+ system("bundle exec rake db:migrate")
9
+ end
10
+ end
11
+
12
+ def seed_db(path)
13
+ Dir.chdir(path) do
14
+ system("bundle exec rake db:seed")
15
+ end
16
+ end
17
+
18
+ desc "Generates a dummy app for testing"
19
+ task test_app: "decidim:generate_external_test_app" do
20
+ ENV["RAILS_ENV"] = "test"
21
+ install_module("spec/decidim_dummy_app")
22
+ end
23
+
24
+ desc "Generates a development app"
25
+ task :development_app do
26
+ Bundler.with_original_env do
27
+ generate_decidim_app(
28
+ "development_app",
29
+ "--app_name",
30
+ "#{base_app_name}_development_app",
31
+ "--path",
32
+ "..",
33
+ "--recreate_db",
34
+ "--demo"
35
+ )
36
+ end
37
+
38
+ install_module("development_app")
39
+ seed_db("development_app")
40
+ end
@@ -0,0 +1,4 @@
1
+ /*
2
+ *= link decidim/comparative_stats/graphs.css
3
+ *= link decidim/comparative_stats/geocoded_events.css
4
+ */
@@ -0,0 +1,2 @@
1
+ //= link decidim/comparative_stats/graphs.js
2
+ //= link decidim/comparative_stats/geocoded_events.js
@@ -0,0 +1,4 @@
1
+ /*
2
+ *= link decidim/comparative_stats/widget.css
3
+ *= link decidim/comparative_stats/geocoded_events.css
4
+ */
@@ -0,0 +1,2 @@
1
+ //= link decidim/comparative_stats/graphs.js
2
+ //= link decidim/comparative_stats/geocoded_events.js
@@ -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.94zm9.5 13.7H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zm0 3.68H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zM22.26 23H8a1 1 0 0 1 0-1.94h14.26a1 1 0 0 1 0 1.94z"/></svg>
@@ -0,0 +1,94 @@
1
+ // = require leaflet
2
+ // = require leaflet-svg-icon
3
+ // = require leaflet.markercluster
4
+ // = require jquery-tmpl
5
+ // = require_self
6
+
7
+ $(() => {
8
+
9
+ var map_object = document.getElementById('geocoded_events');
10
+ var map = L.map(map_object, {
11
+ center: [41,2],
12
+ zoom: 4
13
+ });
14
+
15
+ L.DivIcon.SVGIcon.DecidimIcon = L.DivIcon.SVGIcon.extend({
16
+ options: {
17
+ fillColor: "#ef604d",
18
+ opacity: 0
19
+ },
20
+ _createPathDescription: function() {
21
+ return "M14 1.17a11.685 11.685 0 0 0-11.685 11.685c0 11.25 10.23 20.61 10.665 21a1.5 1.5 0 0 0 2.025 0c0.435-.435 10.665-9.81 10.665-21A11.685 11.685 0 0 0 14 1.17Zm0 17.415A5.085 5.085 0 1 1 19.085 13.5 5.085 5.085 0 0 1 14 18.585Z";
22
+ },
23
+ _createCircle: function() {
24
+ return ""
25
+ }
26
+ });
27
+
28
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
29
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
30
+ }).addTo(map);
31
+
32
+ var endpoints = $('#geocoded_events').data('geocoded-events');
33
+ const popupProposalsTemplateId = "marker-popup-proposal";
34
+ $.template(popupProposalsTemplateId, $(`#${popupProposalsTemplateId}`).html());
35
+ const popupMeetingsTemplateId = "marker-popup-meeting";
36
+ $.template(popupMeetingsTemplateId, $(`#${popupMeetingsTemplateId}`).html());
37
+
38
+ const randomColor = () => '#'+Math.floor(Math.random()*16777215).toString(16);
39
+
40
+ const getMarker = (id, point, color) => {
41
+ let coordinates = [point.latitude, point.longitude];
42
+ let marker = L.marker(coordinates, {
43
+ icon: new L.DivIcon.SVGIcon.DecidimIcon({fillColor: color})
44
+ });
45
+
46
+ let node = document.createElement("div");
47
+
48
+ $.tmpl(id, point).appendTo(node);
49
+ marker.bindPopup(node, {
50
+ maxWidth: 640,
51
+ minWidth: 500,
52
+ keepInView: true,
53
+ className: "map-info"
54
+ }).openPopup();
55
+
56
+ return marker;
57
+ };
58
+
59
+ let layers = {};
60
+
61
+ for (let endpoint in endpoints) {
62
+
63
+ // In the future the color should come from the assigned to the endpoint
64
+ let color = randomColor();
65
+ let markerClusters = L.markerClusterGroup({
66
+ iconCreateFunction: function (cluster) {
67
+ var childCount = cluster.getChildCount();
68
+
69
+ return new L.DivIcon({ html: '<div style="background-color:' + color +'"><span>' + childCount + '</span></div>', className: 'marker-cluster', iconSize: new L.Point(40, 40) });
70
+
71
+ }
72
+ });
73
+
74
+ let meetings = endpoints[endpoint].meetings;
75
+ let proposals = endpoints[endpoint].proposals;
76
+
77
+ for(let key in meetings) {
78
+ markerClusters.addLayer(getMarker(popupMeetingsTemplateId, meetings[key], color));
79
+ }
80
+
81
+ for(let key in proposals) {
82
+ markerClusters.addLayer(getMarker(popupProposalsTemplateId, proposals[key], color));
83
+ }
84
+ markerClusters.addTo(map);
85
+ let key = '<span class="endpoint-legend"><span class="square" style="background-color:' + color +'"></span>' + endpoints[endpoint].name + '</span>';
86
+ layers[key] = markerClusters;
87
+ }
88
+
89
+ L.control.layers(null, layers).addTo(map);
90
+
91
+ $("[data-tabs]").on('change.zf.tabs', function() {
92
+ map.invalidateSize(true);
93
+ });
94
+ });
@@ -0,0 +1,13 @@
1
+ // = require chartkick
2
+ // = require Chart.bundle
3
+ // = require_self
4
+
5
+ $(() => {
6
+ //create trigger to resizeEnd event
7
+ $(window).resize(() => {
8
+ if(window.resizeTO) clearTimeout(window.resizeTO);
9
+ window.resizeTO = setTimeout(() => {
10
+ $(window).trigger('resizeEnd');
11
+ }, 100);
12
+ });
13
+ });
@@ -0,0 +1,23 @@
1
+ @import "graphs";
2
+ @import "decidim/modules/cards";
3
+ @import "decidim/modules/address";
4
+ @import "decidim/modules/buttons";
5
+ @import "decidim/modules/map";
6
+ @import "decidim/extras/leaflet";
7
+
8
+ #geocoded_events{
9
+ min-height: 360px;
10
+ height: 100%;
11
+
12
+ .endpoint-legend{
13
+ font-size: .8rem;
14
+ line-height: .8;
15
+
16
+ .square{
17
+ display: inline-block;
18
+ width: .9em;
19
+ height: .9em;
20
+ margin-right: .3em;
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,6 @@
1
+ @import "decidim/variables";
2
+ @import "decidim/utils/settings";
3
+ @import "decidim/utils/mixins";
4
+ @import "decidim/modules/typography";
5
+ @import "decidim/modules/margins";
6
+ @import "decidim/modules/buttons";
@@ -0,0 +1,8 @@
1
+ html,
2
+ body{
3
+ height: 100%;
4
+ }
5
+
6
+ .full-height{
7
+ height: calc(100% - 1.5rem);
8
+ }
@@ -0,0 +1,14 @@
1
+ <div class="row small-up-1 smallmedium-up-2 mediumlarge-up-3 large-up-4 card-grid">
2
+ <% metrics.each do |name, metric| %>
3
+ <div class="column" style="padding: 1em">
4
+ <div class="card">
5
+ <div class="card-divider">
6
+ <%= title name %>
7
+ </div>
8
+ <div class="card-section">
9
+ <%= pie_chart metric, id: "pie_chart_#{name}" %>
10
+ </div>
11
+ </div>
12
+ </div>
13
+ <% end %>
14
+ </div>
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "chartkick"
4
+ require "chartkick/helper"
5
+
6
+ module Decidim
7
+ module ComparativeStats
8
+ class MetricPiechartsCell < Decidim::ViewModel
9
+ include Chartkick::Helper
10
+
11
+ def show
12
+ return unless model
13
+
14
+ render :show
15
+ end
16
+
17
+ def endpoints
18
+ model
19
+ end
20
+
21
+ def title(name)
22
+ return t "decidim.comparative_stats.metrics.users.title" if name == "users"
23
+
24
+ t "decidim.metrics.#{name}.title"
25
+ end
26
+
27
+ def metrics
28
+ metrics = {}
29
+ endpoints.each do |endpoint|
30
+ endpoint.api.fetch_global_metrics.data.metrics.each do |item|
31
+ metrics[item.name] ||= {}
32
+ metrics[item.name][endpoint.name] = item.count
33
+ end
34
+ end
35
+ metrics
36
+ end
37
+ end
38
+ end
39
+ end