How to get the cell position in spreadsheet in ruby on rails? - ruby-on-rails

Is there any way to get the position(Co-ordinates) of spreadsheet cell in ruby on rails using gem like roo, spreadsheet etc. ?
Suppose I have value "TOTAL" in Cth column and 2nd row. How to get this position using ruby on rails?
Any hints?

#http://spreadsheet.rubyforge.org/GUIDE_txt.html
require 'spreadsheet'
book = Spreadsheet.open 'sample.xls'
sheet1 = book.worksheet 0
sheet1.each_with_index do |row, index|
for column in 0..(row.length-1)
puts "Row:- #{index}, Column: #{column}, Value: #{row[column]}"
end
end

Here is an example from roo official tutorials http://roo.rubyforge.org/
Hope it would help
HOURLY_RATE = 123.45
oo = Openoffice.new("simple_spreadsheet.ods")
oo.default_sheet = oo.sheets.first
4.upto(12) do |line|
date = oo.cell(line,'A')
start_time = oo.cell(line,'B')
end_time = oo.cell(line,'C')
pause = oo.cell(line,'D')
sum = (end_time - start_time) - pause
comment = oo.cell(line,'F')
amount = sum * HOURLY_RATE
if date
puts "#{date}\t#{sum}\t#{amount}\t#{comment}"
end
end

Related

How I get the row number in a iteration using RubyXL?

Using RubyXL I want to know what row number my iteration is.
workbook = RubyXL::Parser.parse("./file.xlsx")
worksheet = workbook[0]
worksheet.each do |row|
test0 = row[0].value
line = ????
puts "Line number #{line} - Value = #{test0}"
end
You can use #each_with_index and write it like this:
workbook = RubyXL::Parser.parse("./file.xlsx")
workbook.first.each_with_index do |row, index|
puts "Line number #{index} - Value = #{row[0].value}"
end
You can use each_with_index method while looping to get the current row number of the iteration
worksheet.each_with_index do |row, index|
test0 = row[0].value
line = index
puts "Line number #{line} - Value = #{test0}"
end

Finding the months between two dates in rails

I can currently set a time range like so:
start_date: "2018-09-11"
end_date: "2018-11-19"
How can I do this for start to end of months? Examples:
time_range = ["2018-09-11".."2018-09-30"]
time_range = ["2018-10-01".."2018-10-31"]
time_range = ["2018-11-01".."2018-11-19"]
I'm not sure what's exactly your desired outcome but, given start date and end date as Date objects, you can perform
(start_date..end_date).to_a.group_by(&:month).values
and at the end what you get is a three element array, and each element contains an array with all the dates in that range for a month
I do not know if I understand very well what you asked, but I'll try to help you.
The Date class has several methods that will help you to work with dates.
Date < Object
Examples
my_date_range_array = [Date.today.beginning_of_year..Date.today.end_of_year]
my_date_time_range_array = [Time.now.beginning_of_year..Time.now.end_of_year]
my_date_range_array = [6.months.ago..Date.today]
YourModel.where date: Date.today.beginning_of_month..Date.today
YourModel.where date: 6.months.ago..Date.today
If you need every single date in the range, you can use something like this:
(Date.today.beginning_of_year..Date.today.end_of_year).map{ |date| date }
I hope that my answer helps you
This is a pure Ruby solution, but I believe (though I don't know Rails) it can be simplified slightly by replacing my methods first_day_of_month and first_day_of_month with Rails methods beginning_of_month and end_of_month, respectively. I designed the method for efficiency over simplicity.
require 'date'
DATE_FMT = "%Y-%m-%d"
def date_ranges(start_date_str, end_date_str)
start_date = Date.strptime(start_date_str, DATE_FMT)
end_date = Date.strptime(end_date_str, DATE_FMT)
return [start_date_str..end_date_str] if
[start_date.year, start_date.month] == [end_date.year, end_date.month]
d = start_date
ranges = [start_date_str..last_day_of_month(d)]
loop do
d = d >> 1
break if [d.year, d.month] == [end_date.year, end_date.month]
ranges << (first_day_of_month(d)..last_day_of_month(d))
end
ranges << (first_day_of_month(d)..end_date_str)
end
def first_day_of_month(d)
(d - d.day + 1).strftime(DATE_FMT)
end
def last_day_of_month(d)
((d >> 1)-d.day).strftime(DATE_FMT)
end
date_ranges("2018-09-11", "2019-02-11")
#=> ["2018-09-11".."2018-09-30", "2018-10-01".."2018-10-31",
# "2018-11-01".."2018-11-30", "2018-12-01".."2018-12-31",
# "2019-01-01".."2019-01-31", "2019-02-01".."2019-02-11"]
date_ranges("2018-09-08", "2018-09-23")
#=> ["2018-09-08".."2018-09-23"]
With the information provided by the OP, this is what I understand he is looking for.
Given a set range for example:
time_range = "2018-09-11".."2018-09-19"
new_range_min = time_range.min.to_date.beginning_of_month
new_range_max = time_range.max.to_date.end_of_month
new_range = new_range_min..new_range_max

AXLSX merge cells inside a style

I'm using ruby gem axlsx and want to know if there's a way to set merge columns inside a style? Today i doing like this:
sheet.merge_cells "A1:E1"
sheet.add_row [I18n.t('foo.some_label').upcase], style: [title]
sheet.merge_cells "B2:E2"
...
I want to avoid to increment the cells manually (B2:E2...B5:E5), there's a way to do this?
Yes give this a try (*Disclaimer I did not actually test these methods but I have used similar functionality in the past)
def merge_last_row(sheet,options ={})
last_row = sheet.rows.last.index + 1
first_col,last_col = options[:columns]
if first_col && last_col
sheet.merge_cells "#{first_col}#{last_row}:#{last_col}#{last_row}"
else
sheet.merge_cells sheet.rows.last
end
sheet.rows.last.style = style if options[:style]
end
so to do what you want it would be
merge_last_row sheet, columns:["A","E"]
sheet.add_row [I18n.t('foo.some_label').upcase]
merge_last_row sheet, columns:["B","E"], style:title
If the last row contains data in A-E then the columns can be left empty and it will merge the whole row. If it does not you could add an option for filling the columns like so
def fill_columns(sheet,column_count,options={})
row = options[:row_data] || []
(column_count - row.count).times do
row << nil
end
sheet.add_row row
end
calls as
my_row = ["Hello","World"]
fill_columns sheet, 5,row_data: my_row
# this will add a row like["Hello","World",nil,nil,nil]
# so that it will merge properly across the columns A-E
merge_last_row sheet
If you are going to use these consistently then patching these functions into Worksheet might make more sense so you don't have to pass the sheet object.
module Axlsx
class Worksheet
def merge_last_row(options={})
last_row = rows.last.index + 1
first_col,last_col = options[:columns]
if first_col && last_col
merge_cells "#{first_col}#{last_row}:#{last_col}#{last_row}"
else
merge_cells rows.last
end
rows.last.style = style if options[:style]
end
def fill_columns(column_count,options={})
row_data = options[:row_data] || []
(column_count - row.count).times do
row_data << nil
end
add_row row_data
end
end
end
Call
sheet.merge_last_row columns:["A","E"]
sheet.add_row [I18n.t('foo.some_label').upcase]
sheet.merge_last_row columns:["B","E"], style:title

Rails: Activity log by week - filling in blank weeks

I'm creating an activity chart, showing the number of records saved to a user's profile over time (in this case its 'user.notes' i.e. user has_many notes). The json output from the function below feeds nicely into a chart library.
The function below however does not output data for weeks without any activity...I need these 'holes', with correct week dates to be in there... can anyone suggest how I might be able to do this?
I believe the 'has_activity' gem has this functionality, however I would prefer to avoid using a whole gem to do this.
class Case < ActiveRecord::Base
def self.user_activity_profile(user)
array = []
user.notes.group_by{ |u| u.created_at.beginning_of_week }.each do |week, entries|
array << { week: week.strftime("%d %b"), count: entries.count }
end
array.to_json
end
end
I believe this should do what you're looking for. It just takes the first and last note for each user and then steps through the date range by 7 days at a time which forces it not to skip any weeks.
class Case < ActiveRecord::Base
def self.user_activity_profile(user)
array = []
notes = user.notes
first = notes.first.created_at.beginning_of_week
last = notes.last.created_at.beginning_of_week
(first..last).step(7){ |date|
array << [
week:date.strftime("%d %b"),
count: notes.where("created_at BETWEEN ? AND ?", date, date + 1.week).count
]}
array.to_json
end
end
def self.user_activity_profile(user)
from = Time.now - 1.months
to = Time.now
tmp = from
array = []
begin
tmp += 1.week
array << [
week: tmp.strftime("%d %b"),
count: user.notes.where("created_at BETWEEN ? AND ?", tmp, tmp + 1.week).count
]
end while tmp <= to
array.to_json
end

Using Column headers to parse excel sheets using roo - Ruby

Can we use column headers to specify the column number from which we are parsing the excel sheet using roo gem? My current code is like this now:
oo = Openoffice.new("simple_spreadsheet.ods")
oo.default_sheet = oo.sheets.first
(2..oo.last_row).each do |line|
date = oo.cell(line,'A')
start_time = oo.cell(line,'B')
end_time = oo.cell(line,'C')
pause = oo.cell(line,'D')
...
end
I would like to parse from column headers instead of specifying columns as 'A' 'B' 'C' ... Can I acheive this using Roo?
You can grab the entire header row as an array and hash the entire row key'd on the header row.
oo = Openoffice.new("simple_spreadsheet.ods")
oo.default_sheet = oo.sheets.first
header = oo.row(1)
2.upto(oo.last_row) do |line|
row_data = Hash[header.zip oo.row(line)]
...
end
You could also use row_data[line] to nest the hashes for later use.
A cleaner/clearer version of the above is
oo = Openoffice.new("simple_spreadsheet.ods")
oo.default_sheet = file.sheets.first
header = oo.first_row
2.upto(oo.last_row) do |line|
row_data = Hash[*header.zip(row).flatten]
...
end
the original took me a bit to understand because especially as i thought hash was a local variable named hash instead of the class Hash
This will use the header row as the keys. The helpful parts are transpose and strip.
def self.excel_to_hash(folder_name, file_name, tab_name)
# Takes an excel file name and a tab name, and returns an array of stripped, transposed rows
# Sample call: my_data = excel_to_hash File.join(Rails.root,'db/data/data_to_import.xlsx'), 'models'
rows = []
file = File.open(File.join(folder_name, file_name), mode = 'r')
excel = Excelx.new(file.path, nil, :ignore)
excel.default_sheet = excel.sheets.index(tab_name) + 1
header = excel.row(1)
(2..excel.last_row).each do |i|
next unless excel.row(i)[0]
row = Hash[[header, excel.row(i)].transpose]
row.each_key{|x| row[x] = row[x].to_s.strip if row[x]}
rows << row
end
return rows
end
valid through Roo gem 1.10.2
This works for me
require 'roo'
# open excel file
excel_file = Roo::Spreadsheet.open(file_path)
# iterate on each sheet
excel_file.each_with_pagename do |name, sheet|
# iterate on each sheet
sheet.parse(headers: true, pad_cells: true) do |row|
# data should be access by column header if we have column header Name we can access like this
row['Name']
end
end
end

Resources