We have an Java application which reads information from DB and generates HTML table via Freemarker like this:
<#if marks?size != 0>
<div>
<p>
<b>Total rows with information about broken utm-marks for ${date} is: ${total}. Displayed in current report: ${displayed}</b>
</p>
<br/>
</div>
<table border="1" cellspacing="0" cellpadding="1">
<tr class="tableHeader" style = "background-color:#f8f5e4; text-align:center; font-weight: bold;">
<th>Report date</th>
<th>Account Login</th>
<th>View Id</th>
<th>Utm marks</th>
<th>Exception type</th>
<th>Exception message</th>
</tr>
<#list marks as mark>
<tr class="tableBody">
<td>${(mark.reportDate)!""}</td>
<td>${(mark.accountLogin)!""}</td>
<td>${(mark.accountViewId)!""}</td>
<td>${(mark.utmMarks)!""}</td>
<td>${(mark.exceptionType)!""}</td>
<td>${(mark.exceptionMessage)!""}</td>
</tr>
</#list>
</table>
<br/>
<#else>
<div>
<p>
<b>No information about broken utm marks for ${date}.</b>
</p>
</div>
</#if>
This generated table will be sent to configured email.
Is it possible to build this type of application with Apache NiFi (without and with ExecuteScript)? Read from DB - fine; send email - fine; but what about templates and html table?
create ./templates folder in nifi root folder
put there a template file test.ftlh with content:
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome ${user}!</h1>
<p>Our latest product:
${latestProduct.name}!
</body>
</html>
use GenerateFlowFile to inject following json into flow file:
{
"user":"Big Joe",
"latestProduct": {
"name":"green mouse",
"url":"aaa/bbb/ccc"
}
}
use ExecuteGroovyScript to merge template with data
#Grab(group='org.freemarker', module='freemarker', version='2.3.31')
import freemarker.template.*
import groovy.json.*
class Const{
static Configuration cfg
}
//on processor start
static onStart(ProcessContext context){
Const.cfg = new Configuration(Configuration.VERSION_2_3_29)
Const.cfg.with{
setDirectoryForTemplateLoading(new File("./templates"))
setDefaultEncoding("UTF-8")
setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER)
setLogTemplateExceptions(false)
setWrapUncheckedExceptions(true)
setFallbackOnNullLoopVariable(false)
}
}
//flowfile process
def ff=session.get()
if(!ff)return
ff.write{InputStream rawIn, OutputStream rawOut->
//assume json in flowfile
def root = new JsonSlurper().parse(rawIn)
Template tpl = Const.cfg.getTemplate("test.ftlh")
rawOut.withWriter("UTF-8"){w-> tpl.process(root, w) }
}
REL_SUCCESS << ff
One way to do this without ExecuteScript (or similiar) would be:
put the HTML template-text in a NiFi process group variable
then use NiFi expression language function evaluateELString to substitute the flowfile attributes into the template.
This approach would have the advantage of being easy to maintain: the HTML template-text could be with the process group; there is no scripting code to maintain.
To illustrate this approach, suppose you put the HTML template-text into a process group variable, named email_template. Further, suppose a flowfile has the attributes set that are required by the HTML template (e.g., from a database). Then to get the realized email body, put the NiFi expression ${email_template:evaluateELString()} in the value for the Replacement Value property of the ReplaceText processor (which precedes the PutEmail processor).
In the illustration above, the variable,email_template could be set to:
<#if marks?size != 0>
<div>
<p>
<b>Total rows with information about broken utm-marks for ${date} is: ${total}. Displayed in current report: ${displayed}</b>
</p>
<br/>
</div>
<table border="1" cellspacing="0" cellpadding="1">
<tr class="tableHeader" style = "background-color:#f8f5e4; text-align:center; font-weight: bold;">
<th>Report date</th>
<th>Account Login</th>
<th>View Id</th>
<th>Utm marks</th>
<th>Exception type</th>
<th>Exception message</th>
</tr>
<#list marks as mark>
<tr class="tableBody">
<td>${mark.reportDate}</td>
<td>${mark.accountLogin}</td>
<td>${mark.accountViewId}</td>
<td>${mark.utmMarks}</td>
<td>${mark.exceptionType}</td>
<td>${mark.exceptionMessage}</td>
</tr>
</#list>
</table>
<br/>
<#else>
<div>
<p>
<b>No information about broken utm marks for ${date}.</b>
</p>
</div>
</#if>
...and ${email_template:evaluateELString()} would substitute the flowfile attribute values for date, total, displayed, mark.reportDate, etc.
Related
I have a rest api returns a Json value as a Output of the service call.
eg:- https://localhost:8080/getEmployees/loadAll
this returns following json values
eg:-
{
"employees":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}
]
}
I need to load the following json values to my thymeleaf table.
In normal way returning values in controller using modal in spring can retun values as list like following.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Employee List</title>
</head>
<body>
<h1>Welcome</h1>
<br>
<h3>Employee List</h3>
<br />
<table border="1">
<tr>
<td>Employee First Name</td>
<td>Employee Last Name</td>
</tr>
<tr th:each="emp : ${empList}">
<td th:text="${emp.firstName}">First Name</td>
<td th:text="${emp.name}">Last Name</td>
</tr>
</table>
</body>
</html>
is there a way to accomplish this using above json using thymeleaf?
You can do something like that using the following structure.
When you call the service
https://localhost:8080/getEmployees/loadAll
you will need to pass the employees data using model.addAttribute.
For instance, let's say you have the following method:
#RequestMapping(value="/getEmployees/loadAll")
String getAllEmployees(Model model) {
model.addAttribute("empList", <your service here that generates the data>);
return "pagenamehere";
}
The above method, will only be executed when you make a call using the following url: https://localhost:8080/getEmployees/loadAll
and it will add your empList data as an attribute. Then, the return string indicates the name of the page that will load. You will need to use your own page with the thymeleaf code.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Employee List</title>
</head>
<body>
<h1>Welcome</h1>
<br>
<h3>Employee List</h3>
<br />
<table border="1">
<tr>
<td>Employee First Name</td>
<td>Employee Last Name</td>
</tr>
<tr th:each="emp : ${empList}">
<td th:text="${emp.firstName}">First Name</td>
<td th:text="${emp.lastNname}">Last Name</td>
</tr>
</table>
</body>
</html>
Now, thymeleaf will be able to display the given data.
I think that you are a little confused. Thymeleaf templates are compiled on server side generating html code. Then, no thymeleaf code found on client side.
The json data got of the api response is generated on client side.
One way is use javascript to load the api response data into a html table.
Another way can you take is modify the controller that calls to the thymeleaf template to get the JSon value. If you store this response (on an object List named empList on your example) yo can add the object into the Controller response (Model or ModelAndView objects) as a template attribute.
I need a table structure using kendo binding for which I have a row-template and item-template ,as I had red telrik(kendo) documentation which says only one line is allowed within row-template.The requirement is that I want to have more than one row in row-template.But as soon as I add more than one line It renders only for the first row.
<script type="text/kendo-template" id="tableEditRows">
<tr class="tableRow" data-bind="source:cells" data-template="tableEditCell"></tr>
<tr>
<td >testsal</td>
</tr>
</script>
<script type="text/kendo-template" id="tableEditCell">
<td class="tableCell" align="center">
<p>value</p>
</td>
</script>
<div id="numeric" ></div>
<script>
var table = $('<table class="tableEdit" style="width:200px">' +
'<tbody align="center" data-bind="source:rows" data-template="tableEditRows">');
$("#numeric").append(table);
var viewModel = kendo.observable( {
rows:[{
cells:[{
Id:1,
Value:"asas"
}]
},{
cells:[{
Id:1,
Value:"asas"
}]
}]
});
kendo.bind($("#numeric").get(0), viewModel);
here a link http://dojo.telerik.com/ifoBA/3 to that I am trying to do.
Is there a way to achieve having more than one line in row-template
I was able to solve this issue by making use of static templating as I had a fixed set of rows.I created a html template within which I used a for loop for each row and for each rows I called a item-template within a <tr> tag. Along with this, I had a additional row template to how additional details.below is a code snippet to show what I had done.
<script type="text/html" id="testTemplate" >
#for(var i=0;i<rows.length;i++){#
<tr class="tableRow" data-bind="source:rows[#=i#].cells" data-template='tableEditCell'></tr>
#if(rows[i].index==0){#
<tr >
<td class="tableCell" >
some value
</td>
</tr>
#}#
#}#
</script>
And here is the compiling and appending of template
var table = $('<table class="elvi"><tbody align="center"></tbody></table>');
var template = kendo.template($("#testTemplate").html());
var itemHtml = template(self.viewModel);
table.append(itemHtml);
table.appendTo($(self.element));
How do I remove extra spaces in tile ? Using below throws an error as unknown
node:normalize-space. this is the one I tried.
//td[#class="title"]/text()/normalize-space(.)
<html>
<head>
<body>
<table class="secondhead" width="100%" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="title">
My status Report (ABCDEFGH12160916)
<span style="font-size:14pt;color:#00FF00"> * Live, Billable, CRM *
</span>
</td>
</tr>
</tbody>
</table>
</body>
</head>
</html>
As per XPath functions reference
fn:normalize-space(string)
fn:normalize-space()
Removes leading and trailing spaces from the specified string, and replaces all internal sequences of white space with one and returns the result. If there is no string argument it does the same on the current node
Example: normalize-space(' The XML ')
Result: 'The XML'
So you should be using the following expression instead:
normalize-space(//td[#class="title"]/text())
Check out Using the XPath Extractor in JMeter guide to learn more about dealing with XPath and JSON Path in JMeter
Am parsing a web page with a standard structure as follows:
<html>
<body>
<table>
<tbody>
<tr class="active">
<td>name1</td>
<td>name2</td>
<td>name3</td>
</tr>
</tbody>
</table>
</body>
</html>
For the life of me, I can't access the 'tbody' or 'tr' elements.
response = open('http://my_url')
node = Nokogiri::HTML(response).css('table')
puts node
Returns
#<Nokogiri::XML::Element:0x8294c08c name="table" attributes=[#<Nokogiri::XML::Attr:0x8294c014 name="id" value="beta-users">] children=[#<Nokogiri::XML::Text:0x82953bc0 "\n">]>
I have tried various tricks but can't seem to dig deeper down to a lower-level child than 'table'.
At best, I can get to the lowest-level Text object by using
node.children
but
node.children.text
returns "\n".
Despite searching for some hours am none the wiser how to sort it out. Any thoughts?
There is a non-closed class value in your sample, it should be:
<html>
<body>
<table>
<tbody>
<tr class="active">
<td>name1</td>
<td>name2</td>
<td>name3</td>
</tr>
</tbody>
</table>
</body>
</html>
After correcting this, you can:
node = Nokogiri::HTML(response).css('table tbody tr td')
node.each {|child| puts child.text}
name1
name2
name3
I have some problem getting all the html tags after script using Xpath
my html :
<table dir = "rtl .......">
<tbody>
<script src = "get.aspx?type=js&file=ajax&rev=3"......>
<script language = "JavaScript"......>
<script>..</script>
<tr>
<td id = "jm0x1"some code here...>
<td id = "jm0x2"some code here...>
also a lot of <tr> here....
</tbody>
how i can access all (td id = "jm0x..)
this is the page i want to parse: http://kooora.com/?c=6423
Something like this should work
//td[contains(#id, "jm0x")]
Then you can affine the contains string to the pattern you want.