Ruby Axlsx Gem Unlocking style on whole column - ruby

Using Ruby with the gem axlsx I've been trying to figure out how to set a style for an entire column and I haven't yet found it. The only way I've been able to do this is in a loop, setting each cell in the row to have the style I'd like, e.g.:
unlocked = wb.styles.add_style :locked => false
#...looping code here, assuming add_worksheet has been assigned to sheet
sheet.rows[rowNum].cells[columnNum].style = unlocked
I found out how to set a hidden attribute to an entire column:
sheet.column_info[3].hidden = true
And from the documentation I had thought this line would work:
sheet.column_info[0].style = unlocked
The strangest thing is that this style set line does not fail to apply and indeed querying it after looks to show the style is set to the corresponding number from the add_style return. However, on checking the produced spreadsheet it doesn't show the whole column as unlocked.
I know this can be done on a whole column quite easily using the write_xlsx gem so I wondered if it can be done using axlsx and I just can't find examples of the right syntax to use?

To do this you can use Worksheet#col_style signature is col_style(index,style,options={})
index
The index of the row you want to set the style on (indexing starts at 0). Also index can be a range so if you want to unlock columns A though C then sheet.col_style((0..2),unlocked) will still work appropriately based on the way the code functions.
style
must be a predefined style as it is in the rest of the gem
options ={}
options allows you to set a row offset so if you didn't want to unlock the top n rows you could pass row_offset: n and all the cells in the column after n rows would be unlocked.
Example:
#unlock cells in column A starting at A3
sheet.col_style(0,unlocked,row_offset: 2)
Full Example
require 'axlsx'
p = Axlsx::Package.new
wb = p.workbook
unlocked = wb.styles.add_style(locked: false)
wb.add_worksheet do |sheet|
5.times { sheet.add_row [1,2,3,4,5,6]}
sheet.col_style(3,unlocked)
end
p.serialize('/simple_test.xlsx')
This will create a spreadsheet with 6 columns and the column with 4's (indexing starts at 0) will be unlocked while the rest of the columns remain locked (default).
Additional Info
The return value from this method will be a flat Array of the cells affected.
Note: the code does pretty much exactly what you are doing in your loop. It collects all the cells and then applies the style to each cell in a loop.

Related

Assigning a value to merged cells in Apple Numbers using AppleScript

As part of a longer AppleScript, I'm copying strings from a list variable into ranges of merged cells in a Numbers 10.0 table. E.g., if the list is called form_filler and the first range of merged cells is B6:Y7, I first tried:
set value of cell "B6" to item 1 of form_filler
I thought one addresses merged cells by the top-left cell. But this does something unexpected: it places the string only into cell "B6" and changes the range of merged cells to C6:Y7, excluding the cell I just pasted into. This behavior occurs consistently with different merged cells throughout the table. I then tried:
set value of range "B6:Y7" to item 1 of form_filler
but this returned an error; I can't assign a value to the range.
I'm new to AppleScript, but not programming generally (e.g., Python). What am I missing? Thanks.
It looks like you have to re-merge those cells. Here's code I just tested using my own tell block structure; you should be able to extrapolate from this (if you include your tell block structure I'll edit my code):
tell application "Numbers"
set d to sheet 1 of document 1
tell d
set value of cell "B6" of table 1 of it to "test"
merge range "B6:Y7" of table 1 of it
end tell
end tell
Not sure if this qualifies as a "work-around", but it seems to work, hopefully w/o introducing other issues.

Ruby: Creating a drop down in Excel with a default value

I need to create an Excel document in Ruby. My requirements are: multiple sheets, some basic formatting, ability to create hyperlinks between cells within the document and creating drop downs. I found two gems: axlsx and writeexcel which seem to offer almost everything that I want. The one mising thing is a default value for created drop downs. I ran the data validation examples for both gems and in both cases the cell containing the drop-down is empty until a human being chooses a value.
What I need is a default value displayed in the cell. So, if %w[ maybe, yes, no ] are the allowed values, then I want "maybe" to be displayed in the cell.
I read what seemed relevant parts of the documentation of both gems, but I didn't find any obvious way to say, that I want some value to be selected by default in the drop down.
I'm not emotionally attached to neither axlsx nor writeexcel, if you can suggest any other approach that will give me what I want, I'll be a happy camper.
OK, so I've found a solution to my question with writeexcel gem. I'm not sure if it's stupid or if it's obvious, but it works well enough for me. What I did is write my default value to a cell, and then add a drop down to the same cell. Below is code based on data_validate.rb example file:
#!/usr/bin/ruby -w
# -*- coding: utf-8 -*-
require 'writeexcel'
workbook = WriteExcel.new('default_dropdown.xls')
worksheet = workbook.add_worksheet
worksheet.set_column('A:A', 32)
txt = 'Select a value from a drop down list'
worksheet.write(1, 1, 'open')
worksheet.write(1, 0, txt)
worksheet.data_validation(1, 1,
{
:validate => 'list',
:source => ['open', 'high', 'close']
})
workbook.close

Update existing excel file template formulas using ruby

I had been using spreadsheet to read in a template excel file, modify it and output a new file for the end-user.
As far as I can identify from the documentation spreadsheet provides no way to input or edit formulas in the produced document.
However, the purpose of my script is to read an undefined number of items from a site and enter them into the spreadsheet, then calculate totals and subtotals.
The end user (using excel or libreoffice etc) is then able to make slight modifications to the quantity of items whilst the totals update (due to formulas) as they are accustomed.
I have looked into the writeexcel gem which claims to be able to input formulas, but I can't see how to take an existing template file and modify it to produce my output. I can only create fresh workbooks.
Any tips please? I do not want to use Win32OLE.
This is surprisingly difficult; apparently all Gems for handling Excel files are missing some crucial functionality.
I can think of two approaches for this problem:
use a combination of spreadsheet (to read the Excel file) and use writeexcel (to write the output file)
use an input file that already contains the required formulas on a separate "formula" sheet and copies the formulas to the "real" sheet
Here's a simplistic version of the second approach:
require 'rubygems'
require 'spreadsheet'
Dir.chdir(File.dirname(__FILE__))
# input file, contains this data
# Sheet0: headers + data (for this simple demo, we will generate the data on-the-fly)
# Sheet1: Formula '=SUM(Worksheet1.A2:A255) in cell A1
book = Spreadsheet.open 'in.xls'
sheet = book.worksheet 0
formulasheet = book.worksheet 1
# insert some input data (in a real application,
# this data would already be present in the input sheet)
rows = rand(20) + 1
(1..rows).each do |i|
sheet[i,0] = i
end
# add total at bottom of column C
sheet[rows+1,2] = formulasheet[0,0]
# write output file
book.write 'out.xls'
However, this will fail if
you're using the same column for your input data and your totals (since then, the total will try to include itself in the calculation)

Ruby Watir if then statement

I've looked around the internet but without much success as well as checked through here.
I'm new to ruby watir and mini test but i'm trying to write some automation that checks the value in a select list drop down and if it finds a value it changes it a 2nd value. If it finds the 2nd value the next time it runs i want it to change the first value.
I've created variables of;
$Title1 = "Dr"
$Title2 = "Mr"
and the code I'm trying to run is attempting to use an if, then, else series of statements;
#Title
if $browser.select_list(id: "title") == $Title2 then
$browser.select_list(:id, "title").select($Title1)
else
if $browser.select_list(id: "title") == $Title1 then
$browser.select_list(:id, "title").select($Title2)
end
however, nothing happens and no errors are triggered.
Any help would be greatly appreciated
Problem
The problem is that the if statements are comparing apples to oranges (ie two different things). You are comparing a Watir::SelectList, from $browser.select_list(id: "title"), with a string, from $Title2.
For Watir elements, the == is used to check if two elements are the same element on the page. The element will never equal a string.
Solution
What you actually want to compare to the Title2 is the selected options of the select list. Watir select lists have a selected? method for checking this.
You could do something like:
if $browser.select_list(id: "title").selected?($Title2)
$browser.select_list(:id, "title").select($Title1)
else
$browser.select_list(:id, "title").select($Title2)
end

Using Ruby and Mechanize to make new Olympic medals count

I want to remake the Olympic medals count on London2012 to better reflect the value of the medals. Currently it is only sorted by gold medals. I'd like to relist it by points, so gold=4, silver=2 and bronze=1 to make a new more rational list. I probably want to remember the previous rank then add a new rank column as well.
I'd like to try mechanize to get raw data from site, then parse the data into rows and cols, apply the new counts, then remake the list.
From source at http://www.london2012.com/medals/medal-count/ each country has a block with medals like so:
<span class="countryName">Canada</span></a></div></div></td><td class="gold c">0</td><td class="silver c">2</td><td class="bronze c">5</td>
If I use agent.get('http://www.london2012.com/medals/medal-count') It shows the whole list. How to parse specific spans and table data?
I also need to remember the rank, then when I make the new page put the new rank beside it.
Any tips on mechanize parsing and remembering data would be really helpful. More importantly your thinking process in doing something like this, I'd appreciate the help to get me started. This doesn't have to be a code answer
Thanks
First to identify the table. In chrome load the page and right click anywhere on the table. Go to inspect element. Go up the heirarchy until you're on the table. Now select it and you'll see it looks like this:
<table class="or-tbl overall_medals sortable" summary="Schedule">
The overall_medals class looks like it will be unique so that's a good one to use. Now start irb and do:
require 'mechanize'
agent = Mechanize.new
page = agent.get 'http://www.london2012.com/medals/medal-count/'
double check that the table is unique:
page.search('table.overall_medals').size
#=> 1 (good, it is)
You can get all the data from the table into an array with:
page.search('table.overall_medals tr').map{|tr| tr.search('td').map(&:text)}
Notice that the first 2 rows are empty, let's get rid of them by using a range:
data = page.search('table.overall_medals tr')[2..-1].map{|tr| tr.search('td').map(&:text)}
The second row isn't really empty, it has the column names (in th's instead of td's). You can get those with:
columns = page.search('table.overall_medals tr[2] th').map{|th| th.text.strip}
You can get these into hashes with:
rows = data.map{|row| Hash[columns.zip row]}
Now you can do
rows[0]['Country']
#=> "United States of America"
Or even one big hash:
countries = rows.map{|row| {row['Country'] => row}}.reduce &:merge
now:
countries['France']['Gold']
#=> "8"
You might find this Medals API useful (Assuming your question is not specifically about Mechanize)
http://apify.heroku.com/resources/5014626da8cdbb0002000006
It uses Nokogiri to parse the site and the output is available as JSON:
http://apify.heroku.com/api/olympics2012_medals.json

Resources