Wednesday, July 22, 2015

Reading Files without File Layout

How often we have worked or seen a requirement to read/write data from or to a file. The file can be of any format , be it CSV,Fixed width, Excel or XML.
 
PeopleSoft provides us a peopletools object :”FileLayout ” which definitely does our work in most of cases and makes the read/write a much more simpler task.
But it is not compulsory that we have to use File Layouts when working with files.
 
When the requirement is simple like reading data from a .csv or any delimiter file and load into a table, we can forego File Layout and do our task.
 
This tutorials explains how can we read a .csv file without using File Layout.
 
Let’s take a hypothetical requirement:
Let’s suppose the “PS_SETID_TBL” where all Setids are stored iss loaded by file which comes from a third party. The third party send .csv file with all the details to be loaded in setid table.
 
The PS_SETID_TBL has five columns



So, our input file also has five columns and looks like the below set:




SETID ,    DESCR                         ,    DESCRSHORT ,    PARTIAL_SHARING ,    DESCRLONG
CAN0 ,    Canada Table Sets              ,    Canada – 0 ,    N               ,    NULL
CAN1 ,    Setid for CAN1                 ,    Canada – A ,    N               ,    NULL
CAN2 ,    Setid for CAN1                 ,    Canada – B ,    N               ,    NULL
CAN3 ,    Setid for CAN1                 ,    Canada – C ,    N               ,    NULL
CAN4 ,    Setid for CAN1                 ,    Canada – D ,    N               ,    NULL
CAN5 ,    Setid for CAN1                 ,    Canada – E ,    N               ,    NULL
CAN6 ,    Setid for CAN1                 ,    Canada – F ,    N               ,    NULL
CAN7 ,    Setid for CAN1                 ,    Canada – G ,    N               ,    NULL
CAN8 ,    Setid for CAN1                 ,    Canada – H ,    N               ,    NULL
CAN9 ,    Setid for CAN1                 ,    Canada – J ,    N               ,    NULL
CAN10 ,   Setid for CAN1                 ,    Canada – K ,    N               ,    NULL
 
The first row is optional, sometime we need header row to help the user identify the columns.
We will see how we can skip the header row and read the rows below.

We will create an application engine which when triggered will read the .csv file and then load it into the table.



We have added just one step and one action of peoplecode which will read the file and load the data into PS_SETID_TBL.


Now we have to execute certain steps to read and load the file.
 
1. Get the file from your client or server, you are either running the application engine from 2 tier or 3 tier and accordingly keep the file either on your local machine or server.
 
Here is the data which is in the file, I have remove the extra spaces.


SETID,DESCR,DESCRSHORT,PARTIAL_SHARING,DESCRLONG
CAN0,Canada Table Sets,Canada – 0,N,NULL
CAN1,Setid for CAN1,Canada – A,N,NULL
CAN2,Setid for CAN1,Canada – B,N,NULL
CAN3,Setid for CAN1,Canada – C,N,NULL
CAN4,Setid for CAN1,Canada – D,N,NULL
CAN5,Setid for CAN1,Canada – E,N,NULL
CAN6,Setid for CAN1,Canada – F,N,NULL
CAN7,Setid for CAN1,Canada – G,N,NULL
CAN8,Setid for CAN1,Canada – H,N,NULL
CAN9,Setid for CAN1,Canada – J,N,NULL
CAN10,Setid for CAN1,Canada – K,N,NULL


I will be running the AE from 2-tier .So, I kept the file in my local and will provide the path to the Peoplecode.
 
To Retrieve a file from a path we use the “GetFile” function.
The GetFile function takes the file’s path and mode in which we want to pull.
We also pass 2 more parameters to tell whether the file is for reading or Writing and its path type.
Reading: R
Writing: W
In this case we only want to read the file so we will pass the file’s absolute path and R as the parameter.
 
Local File &FileSetid;
&FileSetid = GetFile(“c:tempsetid.csv”, “R”, %FilePath_Absolute);


Be careful about the file and parameters passed.
The parameters values can be passed in multiple ways in different scenario.
 
2. Read the file row by row till the end.The ReadLine() function.
 
We will use the ReadLine() function to read the file row by row.
ReadLine() function reads a row of data as a single string object and move the cursor the next line.
We can use the while loop as below read the file row by row till the end of data.We can ignore the first line which contains the header values by simply calling the ReadLine function once .


&FileSetid.ReadLine(&RowSetid);/*Ignore first header row*/

/*Start from 2nd row which contains data*/



While &FileSetid.ReadLine(&RowSetid);
 
End-While;
 
3. Split each row into respective columns.
 
The ReadLine() function reads the complete row of data at once.
So, in order to retrieve all our column values , we need to split the row into columns.
We may use the Split() function here.
Split() function takes two parameters, the string to be splitted and separator by which the string should be splitted and returns an array of splitted string.
 
Split(string, separator)
Our row of data is separated by a ‘,’ so, we will pass comma as separator.
&Columns_array = Split(&String, “,”);


So, our above code will be changed to :

&FileSetid.ReadLine(&RowSetid);/*Ignore the first line*/


While &FileSetid.ReadLine(&RowSetid);
&Columns_array = Split(&RowSetid, “,”);
 
End-While;

&Column_array is an array which contains all the columns for a particular row.



4. Load the data into SETID_TBL from the array.
 
The easiest way we can do is by writing a SqlExec and passing these array values to the SQLExec.
 
While &FileSetid.ReadLine(&RowSetid);
&Columns_array = Split(&RowSetid, “,”);

SQLExec(“INSERT INTO PS_SETID_TBL (SETID,DESCR,DESCRSHORT,PARTIAL_SHARING,DESCRLONG) VALUES (:1,:2,:3,:4,:5)”, &Columns_array [1], &Columns_array [2], &Columns_array [3], &Columns_array [4], &Columns_array [5]);

End-While;

So, the whole peopleocode will be as below:
 
Local SQL &SQL;
Local File &FileSetid;
Local array of string &Columns_array;


&FileSetid = GetFile(“c:tempsetid.csv”, “R”, %FilePath_Absolute);


While &FileSetid.ReadLine(&RowSetid);
  
  &Columns_array = Split(&RowSetid, “,”);
  SQLExec(“INSERT INTO PS_SETID_TBL (SETID,DESCR,DESCRSHORT,PARTIAL_SHARING,DESCRLONG) VALUES (:1,:2,:3,:4,:5)”, &Columns_array [1], &Columns_array [2], &Columns_array [3], &Columns_array [4], &Columns_array [5]);
  
End-While;



Lets run the Application Engine.




Backend Values after the run:



And That’s it,, In few lines of code we have read a file and loaded into a physical table.
With use of only three functions Getfile (), ReadLine () and Split () , it was indeed a cakewalk.


The above code can be customized to read any delimited file, FIxed width file or XML file.
We may have to use some other way to split those kind of files.

Implementing Parallel Processing with Application Engine

This summary is not available. Please click here to view the post.

Including Image/Photo in PeopleSoft BI Publisher (XMLP) report

Including Image/Photo in BI Publisher report
 
There are 3 ways to include images in BIP report as stated in Peoplebooks.
  1. Inserting images.
  2. Importing images.
  3. Rendering image field data (BLOB).
 
Actually it depends on nature of how dynamic is your photo in the report.
 
For static images, which are not going to change with different reports, the images can be directly inserted into the report by usual word “Insert Image” feature.
 
For reports which have a small list of images, and one of them is included in the report depending on certain condition.
For cases like this we need to import image from sub-template file.
 
To import an image from a sub-template file:
  1. Embed the .jpg, .gif, or .png into the sub-template file.
For example,
Oracle_Logo
where Oracle_Logo is the actual .jpg, .gif, or .png.
 
  1. Import the sub-template file that includes the image by including the following syntax at the top of the
primary template file:
In this code sample, sub-template_NAME is the registered sub-template ID in the Content
Library.
 
  1. Add the calling details in the primary template at the appropriate location using the following syntax:
In this code sample, logo2x.5 is the name of the component that contains the image in the subtemplate
file.
 
Different subtemplate can be called depending on required condition and corresponding image is printed. This is only useful when we have small list of images otherwise we end up creating too many subtemplate and too many conditions to be added in the report.

The third way, and most common scenario is totally dynamic image such as employee’s images when the report is run for specific employee and report pulls up the image of employee from the database and shows it up in the report.
 
BI Publisher supports the rendering of BLOB (Base64 encoded) data fields in an XML file.

If you are running application on Peopletools 8.54 and Application on 9.2, this is quite an easy task.

Basically it involves only two step:
  1. Changing your PS query properties to include image field.
 
Open the query which has the image field.
Click on the properties hyperlink.
 
Select the check box: Image Data in the box for image fields.
 
This features for Image fields in available from 8.54 and not even in 8.53.
 
  1. The second step is add the image field in your template.
 
 
Double click on it and you will get the code window.
Type the below code.
 
 
Replace the value(C.EMPLOYEE_PHOTO) within quotes according to your fieldname
 
If the photo type is gif,BMP or any other format change the content type accordingly.
e.g. “image/gif”
 
Now upload the RTF template, we are good to go.
 
Run the report and image is pulled up in the report.
 
The image size might not be ideal at the first time, but you may add other parameter to adjust its size.
 
Image size attributes in inches:
Image size attributes in pixels:
<fo:instream-foreign-object content-type=”image/jpg” height=”300 px” width=”400 px”>
Image size attributes in centimeters:
Image size attributes as a percentage of the original dimensions:

If you running on tools version lesser than 8.53 or lower, you may try the next method.
 
It also involves two step:
  1. Modifying your PS query to convert to BLOB image into base64.
 
Oracle comes with the utility for encoding the BLOB data into base64 but has a size limitation and many of the photos in database will cross this limit.
In order to achieve complete encoding of the image field, we can split the content into different parts and encode each part separately.
 
You can make use of the “CASE” statement as shown below.
You may need to write all the case statement as expression and use these expressions as field.
 
 
The PSQUERY SQL should look something like below.

SELECT
   CASE
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 1455 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 1)))
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) <= 1455 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(EMPLOYEE_PHOTO))
   END AS C1,
   CASE
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 2910 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 1456)))
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) <= 2910 AND DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 1455 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) – 1455, 1456)))
   END AS C2,
   CASE
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 4365 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 2911)))
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) <= 4365 AND DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 2910 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) – 2910, 2911)))
   END AS C3,
   CASE
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 5820 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 4366)))
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) <= 5820 AND DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 4365 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) – 4365, 4366)))
   END AS C4,
   CASE
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 7275 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, 1455, 5821)))
     WHEN DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) <= 7275 AND DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) > 5820 THEN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(DBMS_LOB.SUBSTR(EMPLOYEE_PHOTO, DBMS_LOB.GETLENGTH(EMPLOYEE_PHOTO) – 5820, 5821)))
   END AS C5
 FROM PS_EMPL_PHOTO
WHERE EMPLID = ‘KUL705′;
 
 
So you will have 5 extra fields in your PSQUERY as base64 encoding and you may concatenate all of them to get the final data.
 
The concatenation can be done either on PSQUERY level or at the report level.
Concatenation also has limitation of size and it may fail for very large photo.
 
I will be doing concatenation at the report level.
 
  1. Modify the RTF template:
Add a dummy field on the report.
 
Double click on the field to open the code.

concat(EXPR5_5,EXPR5_6,EXPR5_7,EXPR5_8,EXPR5_9)“/>
 
You just need to concatenate all the columns containing the image data in base64 encode.
Use concat() function as shown above.
 
Now we can run the report.
 
 
And the report is successfully printing the image.
The size of the report can be adjusted as explained earlier.

The second method is not tested with tools 8.53 and lower version, so if the second method also doesn’t work for you, you may refer oracle support, there is a document on how to import image in lower tools version.