Include data in chart tags instead of using AJAX

Now that we've moved the logic to generate the events data to a model,
and we've got access to the model in the component rendering the chart,
we can render the data inside the chart instead of doing an extra AJAX
request to get the same data.

Originally, this was problaby done this way so the page wouldn't take
several seconds to load while preparing the data for the chart when
there are thousands of dates being displayed. With an AJAX call, the
page would load as fast as usual, and then the chart would render after
a few seconds. However, we can have an even better performance
improvement in this scenario if we use a Set instead of an Array. The
method `Array#include?`, which we were calling for every date in the
data, is much slower that `Set#merge`. So now both the page and the
chart load as fast as expected.

We could also use something like:

```
def add
  (...)
  shared_keys.push(*collection.keys)
end

def build
  (...)
  shared_keys.uniq.each do |k|
  (...)
end

def shared_keys
  @shared_keys ||= []
end
```

Or other approaches to avoid using `Array#include?`. The performance
would be similar to the one we get when using `Set`. We're using a `Set`
because it makes more obvious that `shared_keys` is supposed to contain
unique elements.

We've had some tests failing in the past due to these AJAX requests
being triggered automatically during the tests and no expectations
checking the requests have finished, so now we're reducing the amount of
flaky tests.
This commit is contained in:
Javi Martín
2024-04-25 02:11:00 +02:00
parent d25f2e7259
commit 14454bdd45
7 changed files with 18 additions and 60 deletions

View File

@@ -5,14 +5,12 @@
var buildGraph; var buildGraph;
buildGraph = function(el) { buildGraph = function(el) {
var conf, url; var conf;
url = $(el).data("graph");
conf = { conf = {
bindto: el, bindto: el,
data: { data: {
x: "x", x: "x",
url: url, json: $(el).data("graph")
mimeType: "json"
}, },
axis: { axis: {
x: { x: {

View File

@@ -16,7 +16,7 @@ class Admin::Stats::ChartComponent < ApplicationComponent
end end
def chart_tag def chart_tag
tag.div("data-graph": admin_api_stats_path(event: event)) tag.div("data-graph": chart.data_points.to_json)
end end
def title def title

View File

@@ -1,9 +0,0 @@
class Admin::Api::StatsController < Admin::Api::BaseController
def show
if params[:event].present?
render json: Ahoy::Chart.new(params[:event]).data_points
else
render json: {}, status: :bad_request
end
end
end

View File

@@ -9,7 +9,7 @@ module Ahoy
# chart # chart
def add(name, collection) def add(name, collection)
collections.push data: collection, name: name collections.push data: collection, name: name
collection.each_key { |key| add_key key } shared_keys.merge(collection.keys)
end end
def build def build
@@ -36,11 +36,7 @@ module Ahoy
end end
def shared_keys def shared_keys
@shared_keys ||= [] @shared_keys ||= Set.new
end
def add_key(key)
shared_keys.push(key) unless shared_keys.include? key
end end
end end
end end

View File

@@ -241,10 +241,6 @@ namespace :admin do
end end
end end
namespace :api do
resource :stats, only: :show
end
resources :geozones, only: [:index, :new, :create, :edit, :update, :destroy] resources :geozones, only: [:index, :new, :create, :edit, :update, :destroy]
namespace :site_customization do namespace :site_customization do

View File

@@ -0,0 +1,13 @@
require "rails_helper"
describe Admin::Stats::ChartComponent do
it "renders a tag with JSON data" do
create(:user, :level_three, verified_at: "2009-09-09")
chart = Ahoy::Chart.new(:level_3_user)
render_inline Admin::Stats::ChartComponent.new(chart)
expect(page).to have_css "h2", exact_text: "Level 3 users verified (1)"
expect(page).to have_css "[data-graph='{\"x\":[\"2009-09-09\"],\"Level 3 users verified\":[1]}']"
end
end

View File

@@ -1,36 +0,0 @@
require "rails_helper"
describe Admin::Api::StatsController, :admin do
describe "GET index" do
context "events or visits not present" do
it "responds with bad_request" do
get :show
expect(response).not_to be_ok
expect(response).to have_http_status 400
end
end
context "events present" do
before do
time_1 = Time.zone.local(2015, 01, 01)
time_2 = Time.zone.local(2015, 01, 02)
time_3 = Time.zone.local(2015, 01, 03)
create(:proposal, created_at: time_1)
create(:proposal, created_at: time_1)
create(:proposal, created_at: time_2)
create(:debate, created_at: time_1)
create(:debate, created_at: time_3)
create(:debate, created_at: time_3)
end
it "returns single events formated for working with c3.js" do
get :show, params: { event: "proposal_created" }
expect(response).to be_ok
expect(response.parsed_body).to eq "x" => ["2015-01-01", "2015-01-02"], "Citizen proposals created" => [2, 1]
end
end
end
end