Delete Nodes from a HTML table using Nokogiri - ruby-on-rails

I have been scratching my head over this for a while. Help me out before I start picking my brain.
I have a html document that has an events table which has 'In' and 'Out' as part of the columns. A record can either be an In or Out event. I wan't to only get the rows with values in the 'In' column and then save the text in an event model with the same attributes. The code below is what I have which returns '0'.
#!/usr/bin/env ruby
require 'rubygems'
require 'nokogiri'
doc = Nokogiri::HTML <<-EOS
<table><thead><th>Reference</th><th>Event Date</th><th>Event Details</th><th>In</th><th>Out</th></thead><tbody><tr><td>BCE16</td><td>2011-08-16 11:14:52</td><td>Received from Arap Moi</td><td>30.00</td><td></td></tr><tr><td>B07K2</td><td>2011-08-16 11:10:06</td><td>Sent out to John Doe.</td><td> </td><td>-50.00</td></tr></tbody><tfoot></tfoot></table>
EOS
minus_received = doc.xpath('//td[contains(text(), "Received from")]').each do |node|
node.parent.remove
end
p minus_received.to_s
Human Readable markup
<table>
<thead>
<th>Reference</th>
<th>Event Date</th>
<th>Event Details</th>
<th>In</th>
<th>Out</th>
</thead>
<tbody>
<tr>
<td>BCE16</td>
<td>2011-08-16 11:14:52</td>
<td>Received from Arap Moi.</td>
<td>30.00</td>
<td></td>
</tr>
<tr>
<td>B07K2</td>
<td>2011-08-16 11:10:06</td>
<td>Sent out to John Doe.</td>
<td> </td>
<td>-50.00</td>
</tr>
</tbody>
<tfoot></tfoot>
</table>
I appreciate your help.

You're outputting the value of .each - if you look at doc after your each call finishes, the html only contains the header and John Doe.

Related

Last Element in List in Thymeleaf

I have a list in Thymeleaf and I want to use th:with in a div to grab the last element in a list. I've tried:
<div th:if="${not #lists.isEmpty(licence.registrations)}"
th:with="lastRegistation=${licence.registrations[__(#lists.size(licence.registrations) - 1)__]}">
<tr>
<td>Name</td>
<td><span th:text="${lastRegistration.name}"/></td>
</tr>
</div>
However this gives me the error:
org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "(#lists.size(licence.registrations) - 1)" (template: "admin/viewLicence.html" - line 103, col 10)
Does anyone know a way I can get the last item in a list with Thymeleaf?
Cheers,
Neil
If you're using preprocessing, you need to surround the expression with ${...}. Like this:
th:with="lastRegistration=${licence.registrations[__${#lists.size(licence.registrations) - 1}__]}"
That being said, there is no reason to use preprocessing in this case. (And I would remove the extra unused tags as well.) This will work:
<tr th:if="${not #lists.isEmpty(licence.registrations)}"
th:with="lastRegistration=${licence.registrations[#lists.size(licence.registrations) - 1]}">
<td>Name</td>
<td th:text="${lastRegistration.name}" />
</tr>
You can also get fancy with collection selection & projection, but I'm not sure this is an appropriate use. Still, it seems to work:
<tr th:if="${not #lists.isEmpty(licence.registrations)}">
<td>Name</td>
<td th:text="${licence.registrations.![name].$[true]}" />
</tr>

Nokogiri move node to parent's sibling

My task is to translate an XML table to an HTML table. The problem is that the XML does not follow HTML convention, I am going to have to move nodes to the right place. The headers are pre-ordered instead of level-ordered, and there are table notes between the last table row and the closing table tag.
I solved the pre-order to level-order conversion issue by computing and creating the HTML using a builder and then replacing the XML table header with the HTML that I generated. But the last issue, which should be simple, has given me a mental blowout. I need to move the <TNOTE> out of the <GPOTABLE> and put it in a <div> immediately after </GPOTABLE>.
The XML data snippet is:
<P>(vi) Grinding wheels or discs for vertical single-spindle disc grinders shall be encircled with hoods to remove the dust generated in the operation. The hoods shall be connected to one or more branch pipes having exhaust volumes as shown in Table D-57.5.</P>
<GPOTABLE CDEF="s15,6,6,6,6" COLS="5" OPTS="L2">
<TTITLE>Table D-57.5—Vertical Spindle Disc Grinder</TTITLE>
<BOXHD>
<CHED H="1">Disc diameter, inches (cm)</CHED>
<CHED H="1">One-half or more of disc covered</CHED>
<CHED H="2">Number <SU>1</SU>
</CHED>
<CHED H="2">Exhaust foot <SU>3</SU>/min.</CHED>
<CHED H="1">Disc not covered</CHED>
<CHED H="2">Number <SU>1</SU>
</CHED>
<CHED H="2">Exhaust foot<SU>3</SU>/min.</CHED>
</BOXHD>
<ROW>
<ENT I="01">Up to 20 (50.8)</ENT>
<ENT>1</ENT>
<ENT>500</ENT>
<ENT>2</ENT>
<ENT>780</ENT>
</ROW>
<!-- ....snip .... -->
<ROW>
<ENT I="01">Over 53 to 72 (134.62 to 182.88)</ENT>
<ENT>2</ENT>
<ENT>3,140</ENT>
<ENT>5</ENT>
<ENT>6,010</ENT>
</ROW>
<TNOTE>
<SU>1</SU> Number of exhaust outlets around periphery of hood, or equal distribution provided by other means.</TNOTE>
</GPOTABLE>
<P>(vii) Grinding and polishing belts shall be provided with hoods to remove dust and dirt generated in the operations and the hoods shall be connected to branch pipes having exhaust volumes as shown in Table D-57.6.</P>
After conversion to HTML, it should look something like this:
<table cdef="s15,6,6,6,6" cols="5" opts="L2">
<caption>Table D-57.5—Vertical Spindle Disc Grinder</caption>
<tr>
<th rowspan="2" colspan="1" class="table_header">Disc diameter, inches (cm)</th>
<th rowspan="1" colspan="2" class="table_header">One-half or more of disc covered</th>
<th rowspan="1" colspan="2" class="table_header">Disc not covered</th>
</tr>
<tr>
<th rowspan="1" colspan="1" class="table_header">Number <su>1</su></th>
<th rowspan="1" colspan="1" class="table_header">Exhaust foot <su>3</su>/min.</th>
<th rowspan="1" colspan="1" class="table_header">Number <su>1</su> </th>
<th rowspan="1" colspan="1" class="table_header">Exhaust foot<su>3</su>/min.</th>
</tr>
<tr>
<td i="01">Up to 20 (50.8)</td>
<td>1</td>
<td>500</td>
<td>2</td>
<td>780</td>
</tr>
<!-- .... snip .... -->
<tr>
<td i="01">Over 53 to 72 (134.62 to 182.88)</td>
<td>2</td>
<td>3,140</td>
<td>5</td>
<td>6,010</td>
</tr>
</table>
<div class='tnote'><su>1</su> Number of exhaust outlets around periphery of hood, or equal distribution provided by other means</div>
Here's what I've got so far:
def xslt_tables(xml_text)
frag = Nokogiri::HTML(xml_text)
frag.xpath("//gpotable").each do |table|
TableConverter.new(table)
table.name = 'table'
end
frag.inner_html
end
class TableConverter
attr_accessor :data, :rows, :columns, :frag
# Expects a nokogiri object (a single <gpotable> node), not merely an html fragment
def initialize(nokogiri_fragment)
#column_index = 0
#frag = nokogiri_fragment
puts "find table size..."
find_table_size()
puts "populating the grid..."
populate_grid()
puts "computing rowspans and colspans, save in #data..."
compute_rowspans_and_colspans()
puts "assemble headers from #data"
nokogiri_headers = html_headers()
puts "replace the boxhd with nokogiri_headers, translate remaining table entities"
replace_nodes(nokogiri_headers)
end
# .... snip ....
def replace_nodes(headers)
# note: this actually changes values in the original nokogiri object!
# I'll leave it to the calling script to change the name to <table>
# #frag.xpath("//gpotable").each do |table|
# puts "renaming //gpotable"
# table.name = 'table'
# end
#frag.xpath("ttitle").each do |cap|
puts "replacing ttitle with caption"
cap.name = 'caption'
end
#frag.xpath("boxhd").each do |old|
puts "replacing boxhd with generated th with computed rowspan and colspan"
old.replace headers
end
#frag.xpath("row").each do |row|
puts "renaming row to tr"
row.name = 'tr'
end
#frag.xpath("tr/ent").each do |ent|
puts "renaming ent to td"
ent.name = 'td'
end
#frag.xpath("tnote").each do |tfoot|
puts "moving tnote"
tfoot.add_next_sibling('tnote')
end
end
end
Obviously, the last block with the tnote is wrong, but I'm stumped on how to tack that node(s) on to the end of #frag.
I'd be grateful for any nudges in the right direction; the Nokogiri tutorial and cheatsheet just don't make any sense to me.
Three hours after posting, the obvious (now that I see it) answer smacks me upside the head...
#frag.xpath("tnote").each do |tfoot|
puts "moving tnote"
tfoot.parent.add_next_sibling(tfoot).name = 'div'
end
Hope this helps someone else.

dartlang: how do you highlight table rows?

I'm new to dart so this might seem like a silly question but I'd like to apply different CSS to different rows based on column values:
<table id="requests">
<tr>
<td>Title</td>
<td>Status</td>
<td>Date</td>
</tr>
<tr>
<td>Foo</td>
<td>Approved</td>
<td>Date</td>
</tr>
<tr>
<td>Bar</td>
<td>Denied</td>
<td>Date</td>
</tr>
<tr>
<td>Ipsum</td>
<td>None</td>
<td>Date</td>
</tr>
</table>
So far I can do this:
queryAll("tr td").classes.add("foo");
But I'm wondering how to get all rows whose columns have the value "Approved".
Here is a quick dirty way to do it from code:
querySelectorAll('tr').where(
(tr)=> tr.children.any(
(td)=>td.text == 'Approved'
)
)
.forEach((approvedTr)=>approvedTr.classes.add('approved'));
But i really recommend you to use a databinding framework (like Polymer, or AngularDart) to set different styles/classes depending on data. It's far more easy to use.
[A new thread was opened about doing this using Polymer]

tableSorter pager not updating after search

I have tablesorter and pager working together and had no problems so far.
Only thing that I can't manage to do correctly is update the pager after a search.
<table>
<thead>
<tr>
<td>Name</td>
<td>Age</td>
<td>Country</td>
</tr>
</thead>
<tbody>
<tr>
<td>Al</td>
<td>18</td>
<td>Germany</td>
</tr>
<tr>
<td>BAl</td>
<td>19</td>
<td>Italy</td>
</tr>
<tr>
<td>CAl</td>
<td>99</td>
<td>Sweden</td>
</tr>
<tr>
<td>DAl</td>
<td>65</td>
<td>Australia</td>
</tr>
<tr>
<td>EAl</td>
<td>1</td>
<td>Germany</td>
</tr>
</tbody>
I initialize the sorter and the pager to display only 2 rows per page, but after searching for 99 year old person still pager will show 1/3 and not 1/1 which is the correct!
Pager still works, is that visual total page count that is wrong!
Any tips?
I am not sure if this will help or not, but I have it working on mine.
Super important note, all the examples the Official Sorter page has does not use a ID of the table, it just says any table tag. (add the # to the reference)
$("#caseInfoTable").tablesorter({widthFixed: true, widgets: ['zebra']});
$("#caseInfoTable").tablesorterPager({container: $("#pager"), positionFixed: false});
[1]
In the jquery.tablesorter.pager.js file I added these two lines at line 110...
c.pagerPositionSet = false;
fixPosition(table);
Those 2 go right before the line
updatePageDisplay(table);
[1] https://forum.jquery.com/topic/jquery-problem-with-tablesorter-pager-plugin-there-should-be-an-option-to-turn-off-absolute-positioning-of-pager-container/

Using Nokogiri to select a block of HTML based on text?

I have the following block of HTML:
<tr>
<th>Consignment Service Code</th>
<td>ND16</td>
</tr>
What I'm ultimately trying to pull is that ND16 string, but to do that, I need to select the <tr> based on the text Consignment Service Code.
I'm using Nokogiri already to parse the HTML, so it'd be great to just keep using that.
So, how can I select that block of HTML based on the text "Consignment Service Code"?
You can do this:
require 'nokogiri'
doc=Nokogiri::HTML::parse <<-eot
<tr>
<th>Consignment Service Code</th>
<td>ND16</td>
</tr>
eot
node = doc.at_xpath("//*[text()='Consignment Service Code']/following-sibling::*[1]")
puts node.text
# >> ND16
Here's an additional try, which might help you to get going:
## parent node
parent_node = doc.at_xpath("//*[text()='Consignment Service Code']/..")
puts parent_node.name # => tr
## to get the child td
puts parent_node.at_xpath("//td").text # => ND16
puts parent_node.to_html
#<tr>
#<th>Consignment Service Code</th>
# <td>ND16</td>
#</tr>
Yet another way.
Use Nokogiri's css method to find the appropriate tr nodes and then select the ones that have the desired text in the th tag. Finally, work with the selected nodes and extract the td values:
require 'nokogiri'
str = '<tr>
<th>Consignment</th>
<td>ND15</td>
</tr>
<tr>
<th>Consignment Service Code</th>
<td>ND16</td>
</tr>
<tr>
<th>Consignment Service Code</th>
<td>ND17</td>
</tr>'
doc = Nokogiri::HTML.parse(str)
nodes = doc.css('tr')
.select{|el|
el.css('th').text =~ /^Consignment Service Code$/
}
nodes.each do |el|
p el.css('td').text
end
Output is:
"ND16"
"ND17"

Resources