nokogiri and xpath -- nested looping with data set - ruby-on-rails

I'm trying to loop through the elements in each but am having issues with the inner loop below. It appears to me the xpath pattern '*/td' is not returning any results. I'm expecting to see the data inside the tags printed to stdout. I'm using nokogiri.
I'm pasting this into my rails console:
require 'nokogiri'
f = File.open("public/index.html")
doc = Nokogiri::HTML(f)
f.close
doc.xpath('//*[#id="WhoIsOnDutyTableLevel4"]/tbody/tr').each do |row|
puts "row= " + row.to_s
row.xpath('*/td').each do |td|
puts "td= " + td
end
end
And here's the output from the console:
row= <tr id="208894">
<td headers="WhoIsOnDutyTableLevel1:header:1">User 1</td>
<td headers="WhoIsOnDutyTableLevel1:header:2">PERSON</td>
<td headers="WhoIsOnDutyTableLevel1:header:3">0</td>
</tr>
row= <tr id="207792">
<td headers="WhoIsOnDutyTableLevel1:header:1">User 2</td>
<td headers="WhoIsOnDutyTableLevel1:header:2">PERSON</td>
<td headers="WhoIsOnDutyTableLevel1:header:3">5</td>
</tr>
=> 0
Here's the html I'm parsing:
<table class="duty-report-level1" id="WhoIsOnDutyTableLevel1">
<caption></caption>
<thead>
<tr>
<th id="WhoIsOnDutyTableLevel1:header:1" class="duty-report-lt-header">c</th>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td headers="WhoIsOnDutyTableLevel1:header:1">
<table class="duty-report-level2" id="WhoIsOnDutyTableLevel2">
<caption></caption>
<thead>
<tr>
<th id="WhoIsOnDutyTableLevel1:header:1">Group Name</th><th id="WhoIsOnDutyTableLevel1:header:2">Group Time Zone</th><th id="WhoIsOnDutyTableLevel1:header:3">Default Devices</th><th id="WhoIsOnDutyTableLevel1:header:4">Supervisors</th>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td headers="WhoIsOnDutyTableLevel1:header:1">Team 1</td><td headers="WhoIsOnDutyTableLevel1:header:2" class="centered-text">US/Pacific</td><td headers="WhoIsOnDutyTableLevel1:header:3" class="centered-text"><img src="/static/images/icon_boolean_false.png" alt="No" border="0"></td><td headers="WhoIsOnDutyTableLevel1:header:4">
<values>
</values>Mgr 1
<br>
</td>
</tr>
<tr>
<td headers="WhoIsOnDutyTableLevel1:header:1" class="no-padding" colspan="4">
<table class="duty-report-level3" id="WhoIsOnDutyTableLevel3">
<caption></caption>
<thead>
<tr>
<th id="WhoIsOnDutyTableLevel1:header:1" class="th-left">a</th><th id="WhoIsOnDutyTableLevel1:header:2" class="">b</th>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td headers="WhoIsOnDutyTableLevel1:header:1" class="no-padding" colspan="2">
<table class="duty-report-level4" id="WhoIsOnDutyTableLevel4">
<caption></caption>
<thead>
<tr>
<th id="WhoIsOnDutyTableLevel1:header:1">Recipient</th><th id="WhoIsOnDutyTableLevel1:header:2">Category</th><th id="WhoIsOnDutyTableLevel1:header:3">Escalation</th>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr id="208894">
<td headers="WhoIsOnDutyTableLevel1:header:1">User 1</td><td headers="WhoIsOnDutyTableLevel1:header:2">PERSON</td><td headers="WhoIsOnDutyTableLevel1:header:3">0</td>
</tr>
<tr id="207792">
<td headers="WhoIsOnDutyTableLevel1:header:1">User 2</td><td headers="WhoIsOnDutyTableLevel1:header:2">PERSON</td><td headers="WhoIsOnDutyTableLevel1:header:3">5</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>

You need a minor change to your XPath:
doc.xpath('//*[#id="WhoIsOnDutyTableLevel4"]/tbody/tr').each do |row|
# puts "row= " + row.to_s
row.xpath('./td').each do |td|
puts "td= " + td.text
end
end
Which outputs:
td= User 1
td= PERSON
td= 0
td= User 2
td= PERSON
td= 5
Using ./td as the XPath for td basically means "from this point look down one".
Personally, unless you absolutely need XPath, I recommend using CSS accessors. They are more readable, and often much more simple:
doc.search('#WhoIsOnDutyTableLevel4 tbody tr').each do |row|
row.search('td').each do |td|
puts "td= " + td.text
end
end
I recommend using search instead of css or xpath and at instead of at_css or at_xpath. There is no real magic going on when you choose one over the other and you only have to remember two different methods.

The XPath expression in the inner loop is evaluated relative to each tr, so you want to use td (which selects children td elements of the context tr) and not */td (which selects grandchildren td elements).
Full code:
doc.xpath('//*[#id="WhoIsOnDutyTableLevel4"]/tbody/tr').each do |row|
puts "row= " + row.to_s
row.xpath('td').each do |td|
puts "td= " + td
end
end

Related

tablesorter data-math-filter returns no results when given a valid css selector

I'm using Mottie's tablesorter plugin with the Math extension. I have a conditionally formatted table - the categories of conditional formatting are indicated in a data-color attribute on the data cell.
In the footer, I have several rows that summarize the values of the conditional formatting. The footer cells are decorated with data-math-filter="[data-color='color1']".
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody>
<tr>
<td data-color='red' style='background-color:red'>
3
</td>
<td data-color='blue' style='background-color:blue'>
7
</td>
</tr>
<tr>
<td data-color='green' style='background-color:green'>
6
</td>
<td data-color='red' style='background-color:red'>
4
</td>
</tbody>
<tfoot>
<tr>
<td data-math-filter='[data-color="red"]' data-math='col-sum'></td>
<td data-math-filter='[data-color="red"]' data-math='col-sum'></td>
</tr>
<tr>
<td data-math-filter='[data-color="green"]' data-math='col-sum'></td>
<td data-math-filter='[data-color="green"]' data-math='col-sum'></td>
</tr>
<tr>
<td data-math-filter='[data-color="blue"]' data-math='col-sum'></td>
<td data-math-filter='[data-color="blue"]' data-math='col-sum'></td>
</tr>
</tfoot>
</table>
My reading of the docs leads me to believe that the math function will filter for the data-elements - but it doesn't seem to work. I've tried a bunch of different CSS filters - nothing seems to work.
What am I doing wrong?

how to add table with nested values

I want to display terms attribute using thymeleaf. I tried like
<table>
<tr th:each="term: ${contractMap.contractTerms}">
<td><table>
<tr th:each="termRow: ${term.rows}">
<td><table>
<tr th:each="atr: ${termRow.attributes}">
<td th:text="${atr.value}"></td>
</tr ></table>
</td>
</tr>
</table>
</td>
</tr>
</table>
But it is not working.

How to set the highchart and the table horizontally?

This is what my highchart looks like atm
I kinda want the chart to be on the right side while the table to be on the left
this is my html:
<div id="words" style="width:70%;float:left;">
<table border="1" class="highchart" data-graph-container-before="1" data-graph-type="column">
<thead>
<tr>
<th>Word</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<tr>
<td>icon</td>
<td>12</td>
</tr>
<tr>
<td>text</td>
<td>12</td>
</tr>
<tr>
<td>your</td>
<td>9</td>
</tr>
<tr>
<td>post</td>
<td>6</td>
</tr>
<tr>
<td>document</td>
<td>5</td>
</tr>
<tr>
<td>with</td>
<td>5</td>
</tr>
<tr>
<td>parentNode</td>
<td>4</td>
</tr>
<tr>
<td>email</td>
<td>4</td>
</tr>
<tr>
<td>com"</td>
<td>4</td>
</tr>
<tr>
<td>googletag</td>
<td>3</td>
</tr>
</tbody>
</table>
What should i do to achieve such output?
If you can change HTML, then simply put table before Highcharts container and set proper styles (float, width). For example you can also use two divs: http://jsfiddle.net/68qdew8g/
<div style="float: left; width: 30%;">
<table border="1" class="highchart" data-graph-container-before="1" data-graph-type="column">
...
</table>
</div>
<div id="words" style="width:70%;float:left;"></div>

Thymeleaf - Suggested approach when tabular data table returns zero records (empty list)?

Given:
<table id="identification-data" class="pure-table">
<thead>
<tr>
<th>Name</th>
<th>DOB</th>
<th>Gender</th>
<tr>
</thead>
<tbody>
<tr th:each="row : ${identificationData}">
<td th:text="${row['Name']}">Brian Smith</td>
<td th:text="${#calendars.format(row['Date of Birth'], 'MM/dd/yyyy')}">10/11/1971</td>
<td th:text="${row['Gender']}">Male</td>
</tr>
</tbody>
</table>
If the collection ${identificationData} is empty - is there a thymeleafy way to show a message like "no data found"?
I could do something on the controller side like:
if (identificationData.isEmpty()){
model.addAttribute("identificationDataNotFound", Boolean.TRUE);
}
model.addAttribute("identificationData", identificationData);
The most "thymeleafy" way that I can think of is to conditionally render a <tbody> containing the "No data found" message if the list is empty. You can use the utility object #lists to check if the list is empty in the UI (saving you one more boolean model attribute)
<tbody th:if="${not #lists.isEmpty(identificationData)}">
<tr th:each="row : ${identificationData}">
...
</tr>
</tbody>
<tbody th:if="${#lists.isEmpty(identificationData)}">
<tr>
<td colspan="3">No Data found</td>
</tr>
</tbody>

Graphviz: Node internal orientation

The following graphviz code:
digraph g {
labelloc="t";
label="Feed creation process";
graph [
rankdir = "LR"
];
node [
fontsize = "16"
shape = "record"
];
edge [];
abc [shape=none, margin=0, rankdir=""
label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR><TD ROWSPAN="3"><FONT COLOR="red">hello</FONT><BR/>world</TD>
<TD COLSPAN="3">b</TD>
<TD ROWSPAN="3" BGCOLOR="lightgrey">g</TD>
<TD ROWSPAN="3">h</TD>
</TR>
<TR><TD>c</TD>
<TD PORT="here">d</TD>
<TD>e</TD>
</TR>
<TR><TD COLSPAN="3">f</TD>
</TR>
</TABLE>>];
}
Gives:
I'd like to rotate the table orientation 90° clockwise, so that the rows will be:
hello world will be on top
f, 'c|d|eandbon the row below, 'c|d|e aligned vertically
g
h
For example (with the text wrongly oriented!):
Is there a way to rotate the node internals without affecting the order of the nodes in the graph?
I've played with the HTML COLSPAN and ROWSPAN and got:
abc2 [shape=none, margin=0, orientation=120.0,
label=
<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR>
<TD COLSPAN="3"><FONT COLOR="red">HELLO</FONT><BR/>world</TD>
</TR>
<TR>
<TD ROWSPAN="3">b</TD>
<TD>c</TD>
<TD ROWSPAN="3">f</TD>
</TR>
<TR>
<TD PORT="here">d</TD>
</TR>
<TR>
<TD>e</TD>
</TR>
<TR>
<TD COLSPAN="3" BGCOLOR="lightgrey">g</TD>
</TR>
<TR>
<TD COLSPAN="3">h</TD>
</TR>
</TABLE>
>
];

Resources