How do I export AEM reports in Excel? - aem

I'd like to to export AEM reports, page activity or component activity reports, in an excel file.
Is this feature is available in AEM or do I have to write custom for this?

The closest you will get is a CSV selector that can convert report data to CSV but even that has limitations (pagination, filters may be ignored depending on the report).
This, AFAIK, is not an OOTB function. There are old posts and blogs out there to show how this can be done on bpth client side (using JS) or server side using CSV writers.
If you are going down the route of writing a custom solution (most likely outcoume), have a look at the CSV text library that is used in acs-commons user CSV import/export utility, that makes the job really easy and is already a part of AEM.
Hope this helps.

The WriteExcel class simply takes the List collection that is used to populate the JTable object and writes the data to an Excel spreadsheet.
This class uses the Java Excel API. The Java Excel API dependency that is required to work with this API is already in the POM dependencies section.
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import jxl.CellView;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.format.UnderlineStyle;
import jxl.write.Formula;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;
public class WriteExcel {
private WritableCellFormat timesBoldUnderline;
private WritableCellFormat times;
private String inputFile;
public void setOutputFile(String inputFile) {
this.inputFile = inputFile;
}
public int write( List<members> memberList) throws IOException, WriteException {
File file = new File(inputFile);
WorkbookSettings wbSettings = new WorkbookSettings();
wbSettings.setLocale(new Locale("en", "EN"));
WritableWorkbook workbook = Workbook.createWorkbook(file, wbSettings);
workbook.createSheet("Comumunity Report", 0);
WritableSheet excelSheet = workbook.getSheet(0);
createLabel(excelSheet) ;
int size = createContent(excelSheet, memberList);
workbook.write();
workbook.close();
return size ;
}
private void createLabel(WritableSheet sheet)
throws WriteException {
// Lets create a times font
WritableFont times10pt = new WritableFont(WritableFont.TIMES, 10);
// Define the cell format
times = new WritableCellFormat(times10pt);
// Lets automatically wrap the cells
times.setWrap(true);
// create create a bold font with unterlines
WritableFont times10ptBoldUnderline = new WritableFont(WritableFont.TIMES, 10, WritableFont.BOLD, false,
UnderlineStyle.SINGLE);
timesBoldUnderline = new WritableCellFormat(times10ptBoldUnderline);
// Lets automatically wrap the cells
timesBoldUnderline.setWrap(true);
CellView cv = new CellView();
cv.setFormat(times);
cv.setFormat(timesBoldUnderline);
cv.setAutosize(true);
// Write a few headers
addCaption(sheet, 0, 0, "Number");
addCaption(sheet, 1, 0, "Points");
addCaption(sheet, 2, 0, "Name");
addCaption(sheet, 3, 0, "Screen Name");
}
private int createContent(WritableSheet sheet, List<members> memberList) throws WriteException,
RowsExceededException {
int size = memberList.size() ;
// This is where we will add Data from the JCR
for (int i = 0; i < size; i++) {
members mem = (members)memberList.get(i) ;
String number = mem.getNum();
String points = mem.getScore();
String name = mem.getName();
String display = mem.getDisplay();
// First column
addLabel(sheet, 0, i+2, number);
// Second column
addLabel(sheet, 1, i+2, points);
// Second column
addLabel(sheet, 2, i+2,name);
// Second column
addLabel(sheet, 3, i+2, display);
}
return size;
}
private void addCaption(WritableSheet sheet, int column, int row, String s)
throws RowsExceededException, WriteException {
Label label;
label = new Label(column, row, s, timesBoldUnderline);
sheet.addCell(label);
}
private void addNumber(WritableSheet sheet, int column, int row,
Integer integer) throws WriteException, RowsExceededException {
Number number;
number = new Number(column, row, integer, times);
sheet.addCell(number);
}
private void addLabel(WritableSheet sheet, int column, int row, String s)
throws WriteException, RowsExceededException {
Label label;
label = new Label(column, row, s, times);
sheet.addCell(label);
}
public int exportExcel( List<members> memberList)
{
try
{
setOutputFile("JCRMembers.xls");
int recs = write( memberList);
return recs ;
}
catch(Exception e)
{
e.printStackTrace();
}
return -1;
}
}

You can follow the steps described here: Adobe Forums
Select your required page to see component report at http://localhost:4502/etc/reports/compreport.html
Now hit the below URL. It gives you JSON output. http://localhost:4502/etc/reports/compreport/jcr:content/report.data.json
Copy paste the generated JSON output at below URL and click on JSON to excel http://www.convertcsv.com/json-to-csv.htm

You need to write your own logic
create a servlet,
construct Table with data
in the response object add below lines
response.setContentType("text/csv");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=\"" + reportName + ".csv\"");
Cookie cookie = new Cookie("fileDownload", "true");
cookie.setMaxAge(-1);
cookie.setPath("/");
response.addCookie(cookie);
once click on the button you will get the report in the Excel format.

Related

Extend HBase Put to avoid original Row Check in add method

HBase Need to export data from one cluster and import it to another with slight modification in row key
As I have referred in above post, need to export the HBase data of table from one cluster and import it into the another cluster by changing row key based on our match pattern
In the "org.apache.hadoop.hbase.mapreduce.Import" we have option to change the ColumnFamily using the args "HBASE_IMPORTER_RENAME_CFS"
I have slightly modified the Import code to support row key change.My code is available in Pastebin
https://pastebin.com/ticgeBb0
Changed the row key using the below code.
private static Cell convertRowKv(Cell kv, Map<byte[], byte[]> rowkeyReplaceMap) {
if (rowkeyReplaceMap != null) {
byte[] oldrowkeyName = CellUtil.cloneRow(kv);
String oldrowkey = Bytes.toString(oldrowkeyName);
Set<byte[]> keys = rowkeyReplaceMap.keySet();
for (byte[] key : keys) {
if (oldrowkey.contains(Bytes.toString(key))) {
byte[] newrowkeyName = rowkeyReplaceMap.get(key);
ByteBuffer buffer = ByteBuffer.wrap(oldrowkeyName);
buffer.get(key);
ByteBuffer newbuffer = buffer.slice();
ByteBuffer bb = ByteBuffer.allocate(newrowkeyName.length + newbuffer.capacity());
byte[] newrowkey = bb.array();
kv = new KeyValue(newrowkey, // row buffer
0, // row offset
newrowkey.length, // row length
kv.getFamilyArray(), // CF buffer
kv.getFamilyOffset(), // CF offset
kv.getFamilyLength(), // CF length
kv.getQualifierArray(), // qualifier buffer
kv.getQualifierOffset(), // qualifier offset
kv.getQualifierLength(), // qualifier length
kv.getTimestamp(), // timestamp
KeyValue.Type.codeToType(kv.getTypeByte()), // KV
// Type
kv.getValueArray(), // value buffer
kv.getValueOffset(), // value offset
kv.getValueLength()); // value length
}
}
}
return kv;
}
Executed the Import
hbase org.apache.hadoop.hbase.mapreduce.ImportWithRowKeyChange -DHBASE_IMPORTER_RENAME_ROW=123:123456 import file:///home/nshsh/export/
The row key has been successfully changed. But while put the Cell in the HBase table, using
"org.apache.hadoop.hbase.client.Put.add(Cell)" we have check as
"the row of the kv is the same as the put as we are changing row key"
Here it fails.
Then I have commented the check in Put class and updated the hbase-client.jar. Also I have tried to write HBasePut which extends Put
public class HBasePut extends Put {
public HBasePut(byte[] row) {
super(row);
// TODO Auto-generated constructor stub
}
public Put add(Cell kv) throws IOException{
byte [] family = CellUtil.cloneFamily(kv);
System.err.print(Bytes.toString(family));
List<Cell> list = getCellList(family);
//Checking that the row of the kv is the same as the put
/*int res = Bytes.compareTo(this.row, 0, row.length,
kv.getRowArray(), kv.getRowOffset(), kv.getRowLength());
if (res != 0) {
throw new WrongRowIOException("The row in " + kv.toString() +
" doesn't match the original one " + Bytes.toStringBinary(this.row));
}*/
list.add(kv);
familyMap.put(family, list);
return this;
}
}
In the Mapreduce, the task always fails with the below exception
2020-07-24 13:37:15,105 WARN [htable-pool1-t1] hbase.HBaseConfiguration: Config option "hbase.regionserver.lease.period" is deprecated. Instead, use "hbase.client.scanner.timeout.period"
2020-07-24 13:37:15,122 INFO [LocalJobRunner Map Task Executor #0] client.AsyncProcess: , tableName=import
2020-07-24 13:37:15,178 INFO [htable-pool1-t1] client.AsyncProcess: #2, table=import, attempt=18/35 failed=7ops, last exception: org.apache.hadoop.hbase.client.WrongRowIOException: org.apache.hadoop.hbase.client.WrongRowIOException: The row in \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00/vfrt:con/1589541180643/Put/vlen=225448/seqid=0 doesn't match the original one 123_abcf
at org.apache.hadoop.hbase.client.Put.add(Put.java:330)
at org.apache.hadoop.hbase.protobuf.ProtobufUtil.toPut(ProtobufUtil.java:574)
at org.apache.hadoop.hbase.regionserver.RSRpcServices.doBatchOp(RSRpcServices.java:744)
at org.apache.hadoop.hbase.regionserver.RSRpcServices.doNonAtomicRegionMutation(RSRpcServices.java:720)
at org.apache.hadoop.hbase.regionserver.RSRpcServices.multi(RSRpcServices.java:2168)
at org.apache.hadoop.hbase.protobuf.generated.ClientProtos$ClientService$2.callBlockingMethod(ClientProtos.java:33656)
at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:2196)
at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:112)
at org.apache.hadoop.hbase.ipc.RpcExecutor.consumerLoop(RpcExecutor.java:133)
at org.apache.hadoop.hbase.ipc.RpcExecutor$1.run(RpcExecutor.java:108)
at java.lang.Thread.run(Thread.java:745)
I don't know where the old Put Class has been referred in the task.
Can someone please help to fix this.

Read message body of an email using Apache Nifi

Is it possible to retrieve the body content of email, email header details and email attachments in Single step using Apache Nifi.
If so Please help me how to achieve this.
It is not possible in a single step unless you write your own processor or script (using ExecuteScript or InvokeScriptedProcessor). However it is possible in a single flow with something like the following:
ConsumePOP3 -> ExtractEmailHeaders -> ExtractEmailAttachments -> ...
At the end of the flow above, you will have one flow file per attachment, each flow file containing the email headers as attributes and the attachment as the content.
You can use the processor "ExecuteScript", not developing custom processor.
import email
import mimetypes
from email.parser import Parser
from org.apache.commons.io import IOUtils
from java.nio.charset import StandardCharsets
from java.io import BufferedReader, InputStreamReader
from org.apache.nifi.processors.script import ExecuteScript
from org.apache.nifi.processor.io import InputStreamCallback
from org.apache.nifi.processor.io import StreamCallback
class PyInputStreamCallback(InputStreamCallback):
_text = None
def __init__(self):
pass
def getText(self) :
return self._text
def process(self, inputStream):
self._text = IOUtils.toString(inputStream, StandardCharsets.UTF_8)
flowFile = session.get()
if flowFile is not None :
reader = PyInputStreamCallback()
session.read(flowFile, reader)
msg = email.message_from_string(reader.getText())
body = ""
if msg.is_multipart():
for part in msg.walk():
ctype = part.get_content_type()
cdispo = str(part.get('Content-Disposition'))
if ctype == 'text/plain' and 'attachment' not in cdispo:
body = part.get_payload(decode=True) # decode
break
else:
body = msg.get_payload(decode=True)
flowFile = session.putAttribute(flowFile, 'msgbody', body.decode('utf-8', 'ignore'))
session.transfer(flowFile, ExecuteScript.REL_SUCCESS)
Screenshot
The gist above posted by #Thomas mostly worked for me.
However, there were cases where the multipart mail was multi-level, i.e. one of the parts was also multipart. To address that, you could use this version of the getTextFromMessage method instead:
private String getTextFromMessage(Part part) throws MessagingException, IOException {
String result = null;
if (part.isMimeType("text/plain")){
// If the part is a plaintext message, just return the content as a String
Object content = part.getContent();
if (content instanceof String) {
result = (String)content;
}
} else if (part.isMimeType("multipart/*")) {
// If the part is a multi-part message, iterate over the sub-parts
MimeMultipart mimeMultipart = (MimeMultipart)part.getContent();
int count = mimeMultipart.getCount();
for (int i = 0; i < count; i ++){
final BodyPart bodyPart = mimeMultipart.getBodyPart(i);
// Inserted from here...
final String text = getTextFromMessage( bodyPart );
if (text != null) {
result = text;
break;
}
}
}
// Just to be safe...
result = (result != null) ? result : "";
return result;
}
Note that it is possible that there is not even a "html/plain" part at all in the email, so you may want to think about how to handle that case as well. For instance, you could loop over the parts again to extract the "text/html" part (if it exists) and deal with that, and so on.

Jasper Reports: Exporting report to multiple files

I'm developing a jrxml template for generate job candidate's resume. The candidates are in my database.
I need to generate a Word file (.docx) for 1 record (by job candidate), as the image below:
How can I make Jasper generate one file for each record of my SQL query? And export these files to Word?
I saw there is a parameter called PAGE_INDEX exporter. But I did not find how to use it ...
Can someone help me please?
Note 1: My reports are not generated by JasperServer. I developed a Java program to generate them and send reports by email.
Note 2: The number of pages for each candidate may be different.
Updating status
I managed to generate one record per file. But I could only generate the file to the first record.
I need to generate other files for the remaining records.
I'm still with the another problem too: how to separate into separate files when the number of pages for each record (candidate entity) can change?
final JRDocxExporter exporter = new JRDocxExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(new java.io.File("/home/admin/resume candidate.docx")));
SimpleDocxReportConfiguration configuration = new SimpleDocxReportConfiguration();
configuration.setPageIndex(0);
exporter.setConfiguration(configuration);
exporter.exportReport();
PROBLEM SOLUTION
I solved the problem by inserting a variable in the footer of each page with the expression: $V{REPORT_COUNT}, which have record count that is in the Detail Band:
After that, the Java program do loop between the pages of JasperPrint object.
So, i locate that element that tells me what page belongs to candidate.
Based on this information and storing candidate index data and its pages (in a HashMap > mapCandPage), I can determine the page that starts and the page ends for each candidate. And that way I can export one document for each candidate record.
public static void main(String args[]) throws Exception {
File relJasperArqFile = new File("Candidate Resume Template.jasper");
Connection conn = ConnectionFactory.getNewConnectionSQLDRIVER();
JasperReport jasperReport = (JasperReport) JRLoader.loadObject(relJasperArqFile);
JasperPrint jasperPrint
= JasperFillManager.fillReport(jasperReport,
null,
conn);
final JRDocxExporter exporter = new JRDocxExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
List<JRPrintPage> listPrintPage = jasperPrint.getPages();
int candIdx = 0;
int fileIdx = 0;
int lastCandIdx = 0;
HashMap<Integer, List<Integer>> mapCandPage = new HashMap<>();
for (int pageIdx = 0; pageIdx < listPrintPage.size(); pageIdx++) {
JRPrintPage page = listPrintPage.get(pageIdx);
candIdx = getCandIdx(page);
if (!mapCandPage.containsKey(candIdx)) {
mapCandPage.put(candIdx, (new ArrayList<>()));
}
mapCandPage.get(candIdx).add(pageIdx);
if (pageIdx > 0 && candIdx != lastCandIdx) {
fileIdx++;
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(new File(String.format("Candidate Resume %d.docx", fileIdx))));
SimpleDocxReportConfiguration configuration = new SimpleDocxReportConfiguration();
configuration.setStartPageIndex(mapCandPage.get(lastCandIdx).get(0));
configuration.setEndPageIndex(mapCandPage.get(lastCandIdx).get(mapCandPage.get(lastCandIdx).size() - 1));
exporter.setConfiguration(configuration);
exporter.exportReport();
}
lastCandIdx = candIdx;
}
fileIdx++;
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(new File(String.format("Candidate Resume %d.docx", fileIdx))));
SimpleDocxReportConfiguration configuration = new SimpleDocxReportConfiguration();
configuration.setStartPageIndex(mapCandPage.get(lastCandIdx).get(0));
configuration.setEndPageIndex(mapCandPage.get(lastCandIdx).get(mapCandPage.get(lastCandIdx).size() - 1));
exporter.setConfiguration(configuration);
exporter.exportReport();
}
public static Integer getCandIdx(JRPrintPage page) {
JRPrintElement lastRowNumber = page.getElements().get(page.getElements().size() - 1);
return Integer.parseInt(((JRTemplatePrintText) lastRowNumber).getFullText());
}
This is a test and my code is not optimized. If anyone has suggestions or a better idea, please post here. Thank you.

How can we access the actual element in Selenium-webdriver (Java)?

In selenium-webdriver (Java),
We can get the GWT element
for eg. WebElement obj = driver.findElement(By.id("gwt-debug-celltable"));
By obj we can get that webelement of the celltable but we won't get the actual celltable. So if want to check the number of records in the celltable using Selenium-Webdriver. What i need to do?
Is it possible? If yes please answer asap.
Yes. You can do it using xpath, somehow:
List<WebElement> elements =
driver.findElements(By.xpath("//table[#id='gwt-debug-celltable']/tbody/tr"));
In elements will be the list of rows. I have not tested this code. But it likes one that we are using in our project.
For the webtable i have refered the below link http://money.rediff.com/gainers/bsc/daily/groupa
from the below code you can get all the values from the webtable
public class MaxFromTable {
public static void main(String[] args) throws ParseException {
WebDriver wd;
System.setProperty("webdriver.chrome.driver","G://chromedriver.exe");
wd= new ChromeDriver();
wd.get("http://money.rediff.com/gainers/bsc/daily/groupa?");
String max;
double m=0,r=0;
//No. of Columns
List col = wd.findElements(By.xpath(".//[#id='leftcontainer']/table/thead/tr/th"));
System.out.println("Total No of columns are : " +col.size());
//No.of rows
List rows = wd.findElements(By.xpath (".//*[#id='leftcontainer']/table/tbody/tr/td[1]"));
System.out.println("Total No of rows are : " + rows.size());
for (int i =1;i<rows.size();i++)
{
max= wd.findElement(By.xpath("html/body/div[1]/div[5]/table/tbody/tr[" + (i+1)+ "]/td[4]")).getText();
NumberFormat f =NumberFormat.getNumberInstance();
Number num = f.parse(max);
max = num.toString();
m = Double.parseDouble(max);
if(m>r)
{
r=m;
}
}
System.out.println("Maximum current price is : "+ r);
}
}

Syncfusion DocIO -- how to insert image (local file) at bookmark using BookmarksNavigator

I have been using Syncfusion DocIO for generating MS Word documents from my .net applications (winforms). So far I have dealt with plain text and it is fairly straightforward to insert text in a word document template where bookmarks serve as reference points for text insertion.
I am navigating the bookmarks using BookmarksNavigator.MoveToBookmark() . Now I need to insert an image at a bookmark but I am at a loss at how to go about it.
Please help...
Thanks.
Specifically for adding it to a bookmark :
//Move to the specified bookmark
bk.MoveToBookmark(bookmark);
//Insert the picture into the specified bookmark location
bk.DeleteBookmarkContent(true);
// we assume the text is a full pathname for an image file
// get the image file
System.Drawing.Image image = System.Drawing.Image.FromFile(sText);
IWParagraph paragraph = new WParagraph(document);
paragraph.AppendPicture(image);
bk.InsertParagraph(paragraph);
private System.Drawing.Image LoadSignature(string sFileName)
{
string sImagePath = sFileName;
System.Drawing.Image image = System.Drawing.Image.FromFile(sImagePath);
return image;
}
private void MergeSignature(WordDocument doc, string sFile, string sBalise)
{
System.Drawing.Image iSignature = LoadSignature(sFile);
WordDocument ImgDoc = new WordDocument();
ImgDoc.AddSection();
ImgDoc.Sections[0].AddParagraph().AppendPicture(iSignature);
if (iSignature != null)
{
TextSelection ts = null ;
Regex pattern = new Regex(sBalise);
ts = doc.Find(pattern);
if (ts != null)
{
doc.ReplaceFirst = true;
doc.Replace(pattern, ImgDoc, false);
}
}
iSignature.Dispose();
}
See here: https://help.syncfusion.com/file-formats/docio/working-with-mailmerge
1) You should create docx file with name "Template.docx". This file will use as template.
In your docx file create Field of type MergeField.
2) Create MergeFiled with name Image:Krishna
3)
using Syncfusion.DocIO.DLS;
using System.Drawing;
public class Source
{
public Image Krishna { get; set; } = Image.FromFile(#"C:\1.png");
}
and generating code:
public static void Generate()
{
WordDocument doc = new WordDocument("Template.docx");
Source data = new Source();
var dataTable = new MailMergeDataTable("", new Source[] { data });
doc.MailMerge.ExecuteGroup(dataTable);
doc.Save("result.docx");
doc.Close();
}