2013-05-05 1 views
1

Я все еще очень новичок в Rails, но двигаюсь довольно плавно, я бы сказал. Поэтому для практики я работаю над тем, что должно быть простым приложением, когда пользователь может ввести свой вес, и эта информация в течение 30 дней отображается им через линейный график Highcharts с использованием Lazy Highcharts gem. Я пошел за Ryan Bates 'Railscast # 223, чтобы начать.Rails App Plotting Zeros в диаграмме Highchart через Lazy_High_Charts Gem

Мой Выпуск:
Итак, входы обнаруживаются кроме того, что в те дни, которые пользователь не вводит значение он получает отображается на графике как «0» (линия опускается до дна график) вместо того, чтобы подключаться к следующей точке в любой день. Не уверен, что, если все, что имеет смысл, так вот скриншот:

Screenshot

Я нашел это решение: Highcharts - Rails Array Includes Empty Date Entries

Однако, когда я реализую первый вариант найти на этой странице (конвертировать 0 в нуль) :

(30.days.ago.to_date..Date.today).map { |date| wt = Weight.pounds_on(date).to_f; wt.zero? ? "null" : wt } 

точки появляются, но линия не делает, ни делать всплывающие подсказки ... это заставляет меня думать, что что-то ломает JS. Похоже, что в консоли ничего не видно ...? Отсюда я подумал, что это может быть вопрос использования опции «connectNulls» Highchart, но это тоже не сработало. При реализации второго варианта (отклонять метод):

(30.days.ago.to_date..Date.today).map { |date| Weight.pounds_on(date).to_f}.reject(&:zero?) 

она полностью удаляет все даты, которые являются нулем из диаграммы, которая портит структуру полностью, потому что значения должны быть отображены на основе дате их created_at.

Итак, вернемся к квадрату, это то, что я оставил (диаграмма, отображающая нули в течение дней без ввода).

Модель:

class Weight < ActiveRecord::Base 
    attr_accessible :notes, :weight_input 

    validates :weight_input, presence: true 
    validates :user_id, presence: true 

    belongs_to :user 

    def self.pounds_on(date) 
    where("date(created_at) = ?", date).pluck(:weight_input).last 
    end 
end 

Контроллер:

def index 
    @weights = current_user.weights.all 

    @startdate = 30.days.ago.to_date 
    @pounds = (30.days.ago.to_date..Date.today).map { |date| Weight.pounds_on(date).to_f } 

    @h = LazyHighCharts::HighChart.new('graph') do |f| 
    f.options[:title][:text] = " " 
    f.options[:chart][:defaultSeriesType] = "area" 
    f.options[:chart][:inverted] = false 
    f.options[:chart][:zoomType] = 'x' 
    f.options[:legend][:layout] = "horizontal" 
    f.options[:legend][:borderWidth] = "0" 
    f.series(:pointInterval => 1.day, :pointStart => @startdate, :name => 'Weight (lbs)', :color => "#2cc9c5", :data => @pounds) 
    f.options[:xAxis] = {:minTickInterval => 1, :type => "datetime", :dateTimeLabelFormats => { day: "%b %e"}, :title => { :text => nil }, :labels => { :enabled => true } } 
    end 

    respond_to do |format| 
    format.html # index.html.erb 
    format.json { render json: @weights } 
    end 
end 

Кто-нибудь есть решение для этого? Наверное, я мог бы все это ошибаться, поэтому любая помощь очень ценится.

ответ

1

Это особенность HighCharts. Вам нужно передать метку времени в свой массив данных и определить ее для набора данных.

Для каждой точки данных я устанавливаю [время, значение] как кортеж.
[ "Date.UTC(#{date.year}, #{date.month}, #{date.day})" , Weight.pounds_on(date).to_f ]

Вам по-прежнему необходимо удалить нуль в формате w/e, который вам нравится, но данные останутся с правильным значением дня.

Я думаю, что вам нужно удалить :pointInterval => 1.day

Общие советы Вы также должны смотреть на оптимизацию вы запрашиваете pound_on, как вы делаете DB вызов на каждую точку на графике.Сделайте Weight.where(:created_at => date_start..date_end).group("Date(created_at)").sum(:weight_input), который даст вам массив дат created_at с суммой за каждый день.

ADDITIONS

Улучшенный SQL Query

Это опирается на SQL, чтобы делать то, что он делает лучше всего. Во-первых, используйте, где можно поместить запрос в нужные записи (в данном случае за последние 30 дней). Выберите нужные поля (created_at и weight_input). Затем запустите внутреннее соединение, которое запускает sub_query, чтобы группировать записи по дням, выбирая максимальное значение created_at. Когда они совпадают, он отталкивает наибольший (или последний введенный) весовой вклад за этот день.

@last_weight_per_day = Weight.where(:created_at => 30.days.ago.beginning_of_day..Time.now.end_of_day) 
select("weights.created_at , weights.weight_input"). 
joins(" 
    inner join ( 
     SELECT weights.weight_input, max(weights.created_at) as max_date 
     FROM weights 
     GROUP BY weights.weight_input , date(weights.created_at) 
) weights_dates on weights.created_at = weights_dates.max_date 
") 

С этим вы должны быть способны @last_weight_per_day так. Это не должно иметь значений 0/nil, если вы подтвердили их в своей БД. И должен быть довольно быстрым в этом тоже.

@pounds = @last_weight_per_day.map{|date| date.weight_input.to_f} 
+0

ОК, начинающий понимать, как его настройка. Я чувствую, что я так близко! Вот что я сделал на основе вашего предложения: '@pounds = (30.days.ago.to_date..Date.today) .map {| date | ["# {date.year}, # {date.month}, # {date.day}", Weight.pounds_on (date) .to_i.zero? ? nil: Weight.pounds_on (date) .to_f]} .compact' Я уверен, что это может быть реорганизовано, но с этой строкой точки отображаются в правильных датах, а всплывающие подсказки работают, но строка не отображается с точки точка. Я использовал компактный метод для удаления nil, но консоль показывает '['2013, 5, 4", null] '(не ноль), поэтому я не знаю, если это проблема .. – Desmond

+0

Закрыть, но даты должны быть быть фактическим Javascript Date call. Вам нужно, чтобы выход был неизолирован, поэтому дата будет оценена. Вызовите raw() или .html_safe. Что касается нулевого. Я бы потянул дату по-другому. Если вы действительно не хотите даты ... не просите их. Вызовите записи из БД и Итерации через даты. (экспоненциально быстрее). Я выкопаю код для вас. – rposborne

+0

Я добавил к вам запрос по основному ответу. Основная цель - вызывать самое новое значение, введенное за день (ввод веса), и возвращать их все в массив весовых объектов. – rposborne