abak-press / resque-reports

Make your custom reports to CSV in background using Resque with simple DSL
MIT License
0 stars 1 forks source link

Разработка DSL для построения фоновых отчетов #1

Open sclinede opened 11 years ago

sclinede commented 11 years ago

Вот пример существующего отчета "Лог заказов" в csv:

Дата КД;Город;Название компании;Ссылка на место КД;ID товара;Цена товара;Кол-во;Сумма заказа;Дата создания товара;Дата актуализации товара;Градусник товара;Рубрика;ID компании;Текст письма;Пакет;Сегмент;Тип конверсионного действия (КД);Ссылка
02.09.2013 05:47;Якутск;Тойота (Toyota) Центр Екатеринбург Запад;http://yakutsk.blizko.dev:8080/products/avtomobil_toyota_alphard_prestizh_akpp_3013156;3013156;2222000,0;1;2222000.0;13.08.2012 16:51;10.01.2013 04:33;90;Toyota Alphard;8630442;"Здравствуйте, mosova! \n\n Мы хотим заказать ""Автомобиль Toyota Alphard Престиж АКПП"" в количестве \n\n С уважением, Федор";1;Легковые автомобили;"";""
03.09.2013 11:23;Якутск;Тойота (Toyota) Центр Екатеринбург Запад;http://yakutsk.blizko.dev:8080/products/avtomobil_toyota_alphard_prestizh_akpp_3031877;3031877;2485000,0;1;2485000.0;13.08.2012 16:36;10.01.2013 04:33;90;Toyota Alphard;8630442;"Здравствуйте, mosova! \n\n Мы хотим заказать ""Автомобиль Toyota Alphard Престиж АКПП"" в количестве \n\n С уважением, Вася";1;Легковые автомобили;"";""

Хотелось бы достаточно просто формировать подобные отчеты, причем с возможностью выполнять формирование в фоне. На первый взгляд DSL для такой задачи видится следующим:

class OrderListsCSVReport < CSVReport
  source :orders

  directory File.join(Rails.root, 'public/files/shared/tmp/order_lists_report')  

  csv_options :col_sep => ','
  encoding CP1251

  table do |order|
    column 'Дата КД', (DateTime.parse(order['created_at']).in_time_zone.strftime('%d.%m.%Y %H:%M') if order['created_at'])
    column 'Город', :city
    column 'Название компании', order['company_name']
    column 'Ссылка на место КД', order['referer']
    column 'ID товара', order['product_id']
    column 'Цена товара', number_with_delimiter(order['product_price'], :separator => ',', :delimiter => '')
    column 'Кол-во', order['amount']
    column 'Сумма заказа', number_with_delimiter(order['total'], :separator => ',', :delimiter => '')
    column 'Дата создания товара', (DateTime.parse(order['product_created_at']).in_time_zone.strftime('%d.%m.%Y %H:%M') if order['product_created_at'])
    column 'Дата актуализации товара', (DateTime.parse(order['product_actualized_at']).in_time_zone.strftime('%d.%m.%Y %H:%M') if order['product_actualized_at'])
    column 'Градусник товара', order['product_rate']
    column 'Рубрика', order['rubric_title']
    column 'ID компании', order['company_id']
    column 'Текст письма', order['content']
    column 'Пакет', order['packet']
    column 'Сегмент', order['segment']
    column 'Тип КД', order['conversion_type']
    column 'Ссылка', order['lead_url']
  end

  create do |date_from, date_to|
    @date_from = date_from
    @date_to = date_to
  end

  def city(order)
    order['company_main_region_name']
  end

  def orders
    ...# запрос данных
  end
end

# работа с отчетом:
report = OrderListsCSVReport.new(date_from, date_to)
if report.exists? # считаю алиас ready? надо оставить, хорошо читается
  send_data(File.read(report.file_name),...)
else
  @job_id = report.bg_build
end
sclinede commented 11 years ago

можно DSL расширить, добавить параметр unique, туда передать параметры конструктора, аля resque-integration:

class OrderListsCSVReport < CSVReport
  source :orders
  unique { |date_from, date_to| [date_from, date_to] }

  directory File.join(Rails.root, 'public/files/shared/tmp/order_lists_report')  

  csv_options :delimiter => ',' ....
  encoding CP1251

  table do |order|
    column 'Дата КД', (DateTime.parse(order['created_at']).in_time_zone.strftime('%d.%m.%Y %H:%M') if order['created_at'])
    ...
    column 'Ссылка', order['lead_url']
  end

  def city(order)
    order['company_main_region_name']
  end

  def initialize(date_from, date_to)
    ...
  end

  def orders
    ...# запрос данных с использованием @date_from, @date_to
  end
end
Strech commented 11 years ago

Тогда так

unique { [date_from, date_to] }

sclinede commented 11 years ago

эм. ну если это блок, то там откуда даты возьмутся?

{ [date_from, date_to] } 

или ты имел ввиду все же:

unique { |date_from, date_to| [date_from, date_to] }
vkuznetsov commented 11 years ago

чем вариант через конструктор не угодил?

sclinede commented 11 years ago

мне кажется неочевидно

sclinede commented 11 years ago

может тогда алиас какой-нибудь понятный сделать, вместо слова super

Strech commented 11 years ago

чет я устал и ничего особо не понимаю

vkuznetsov commented 11 years ago

Можешь делать через unique, но я бы не хотел каждый раз, используя твой DSL, дублировать список аргументов из конструктора в unique. Надо, чтобы по умолчанию какое-то поведение было. И это можно придумать и сделать без всяких там super.

sclinede commented 11 years ago

О. нашел способ перехватить аргументы. Можно тогда все аргументы просто забирать и все, а если надо только выборочные использовать unique Т.е. по-умолчанию:

class OrderListsCSVReport < CSVReport
  source :orders

  directory File.join(Rails.root, 'public/files/shared/tmp/order_lists_report')  

  csv_options :delimiter => ',' ....
  encoding CP1251

  table do |order|
    column 'Дата КД', (DateTime.parse(order['created_at']).in_time_zone.strftime('%d.%m.%Y %H:%M') if order['created_at'])
    ...
    column 'Ссылка', order['lead_url']
  end

  def initialize(date_from, date_to)
    ...
  end

  def orders
    ...# запрос данных
  end
end

а если есть лишние аргументы в конструкторе:

class OrderListsCSVReport < CSVReport
  source :orders
  unique :date_from, :date_to

  directory File.join(Rails.root, 'public/files/shared/tmp/order_lists_report')  

  csv_options :delimiter => ',' ....
  encoding CP1251

  table do |order|
    column 'Дата КД', (DateTime.parse(order['created_at']).in_time_zone.strftime('%d.%m.%Y %H:%M') if order['created_at'])
    ...
    column 'Ссылка', order['lead_url']
  end

  def initialize(date_from, date_to, some_param)
    ...
  end

  def orders
    ...# запрос данных с использованием @date_from, @date_to
  end
end
sclinede commented 11 years ago

Появилась новая идея, а что если заменить source на report_of, вот так:

class OrderListsCSVReport < CSVReport
  report_of :orders, for: [:date_from, :date_to]
  directory File.join(Rails.root, 'public/files/shared/tmp/order_lists_report')  

  csv_options :delimiter => ',' ....
  encoding CP1251

  table do |order|
    ...
  end

  def initialize(date_from, date_to, some_param)
    ...
  end

  def orders
    ...# запрос данных
  end
end

class ProductsCSVReport < CSVReport
  report_of :products # for => [:company_id] - можно опускать, если совпадает с параметрами конструктора
  directory File.join(Rails.root, 'public/files/shared/tmp/products_report')  

  csv_options :delimiter => ',' ....
  encoding CP1251

  table do |product|
    ...
  end

  def initialize(company_id)
    ...
  end

  def products
    ...# запрос данных
  end
end
Strech commented 11 years ago

@sclinede Пора завязывать, это уже не конструктив, а какая-то муть выходит. Делай уже, в реквесте видно будет что плохого осталось

sclinede commented 11 years ago

Сделал первый коммит. "Это" еще не работает, но можно посмотреть код. Надо еще дописать перехват аргументов конструктора. Еще не прописал зависимости и нет тестов.

Strech commented 11 years ago

@sclinede Лучше делай реквестами, смотреть будет проще имхо :)

sclinede commented 11 years ago

Окей. На сегодня я все, а дальше буду в копию ставить к реквестам. Завтра буду тестировать.

Strech commented 11 years ago

Я бы рекомендовал посмотреть на

  1. Форматирование (читать невозможно)
  2. Вопрос - зачем тебе ActiveSupport? Я не нашел там у тебя чего-то мега-крутого, может facets?
sclinede commented 11 years ago

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

sclinede commented 11 years ago

Я только отдельное issue создам, там обсудим, ок ?