donderdag 5 maart 2015

Script Cognos to save a report pdf to local Folder archive with PhantomJS

I wanted to run a report in our Cognos Reporting Tool send a few parameters with it and get the pdf output to save it on a local folder.

Because it was a quite frustrating experience I wanted to share my insights on this blog



Step 1 : Configure File Locations in Cognos


I followed this manual in configuring archive locations http://queryvision.com/reportstofilesystem_individual/ 
The folders get published as a saving to location in the advanced options when running a report.

It's import that the User account which is running the Cognos express Service has write acces to the given file location

Step 2 : Running the report from a URL


After serveral hours reading documentation I learned that the cognos API allows to open a report and even supports sending parameters with it. I found a good reference for this here
http://public.dhe.ibm.com/software/dw/dm/cognos/reporting/scripting_techniques/using_urls_in_cognos_8.pdf

Example URL was
 http://192.168.1.105:19300/p2pd/servlet/dispatch?ui.name=TestingTheAPI&run.outputFormat=PDF&run.prompt=false&p_myparameterfield=XXXX&ui.action=run&b_action=cognosViewer&encoding=UTF-8&ui.object=myreportlocation&ui=m1m2m3m4&ui.backURL=http%3a%2f%2f192.168.1.105  

In the API I however did NOT find anyway to specify the archive location. When running the report manually you must shedule the report (now) to be able to choose a archive location. I think this explains why just calling a specific URL doesn't allow to shedule it.

Step 3: Finding a way to script it

My 3e attempt was the best one, scroll further to see it

Attempt 1: Refactor the HTTP requests with Wireshark ?
At first I examined the HTTP traffic with  Wireshark when running the report Manually. I tried to simulate the requests but found out (the hard way) that it cannot be done this way. On each page Cognos runs some Javascript function that sets SESSION information. Refactoring the request I stumbled a lot on tracking and signature erros (which can be monitored in the 'Cognos Express\logs\cogserver.log' file)

Attempt 2: Script running the report with InternetExplorer.Application
I created a VBscript that logged in, got the report and clicked thru the site. When running the vbscript on the command shell "cscript filename.vbs" I saw the Internet Explorer window doing everything I needed

I had to tackle some issues:
The  InternetExplorer object gets to '_blank' page when loading or opening a page. I wrote an "AttachObject" function for this so you can click further after each action
Sleeping some time between is always necessary and printing the current location of the Ie object is usefull 'msgbox(ie.LocationURL & "-" & ie.LocationName)

Clicking on specific object was also difficult and I wrote a "ClickOn" function to loop through the items of the current page.


 Call logoff  
 Call logon  
 Call test  
 Sub test()  
   Set ie = CreateObject("InternetExplorer.Application")  
      ie.Visible = True  
      ie.navigate("http://192.168.1.105:19300/p2pd/servlet/dispatch?b_action=xts.run&m=portal/cc.xts&m_folder=i7747335B10784A14B04CD6DC5A9DF6E9")  
      Do While ie.Busy Or ie.readyState <> 4  
           WScript.Sleep 50  
      Loop  
      Found = ClickOn(ie,"Name of wanted reportname",8,False,"a")  
      Do While ie.Busy Or ie.readyState <> 4  
        WScript.Sleep 50  

   Loop  
      WScript.Sleep 2000  
      Set ie = AttachObject  
      Found = ClickOn(ie,"Uitvoeren met opties...",0,False,"a")  
      WScript.Sleep 2000  
      Set ie = AttachObject  
      Found = ClickOn(ie,"geavanceerde opties",0,False,"a")  
      WScript.Sleep 2000  
      Set ie = AttachObject  
      ie.Document.getElementsByName("runType")(1).Click ' = True  
      ie.Document.getElementsByName("m_ro_saveOutput")(0).Click   
      ie.Document.getElementsByName("m_ro_archive")(0).Click   
           Found = ClickOn(ie,"Opties bewerken...",1,False,"a")  
           WScript.Sleep 1000  
           Set ie = AttachObject  
           ie.document.getElementsByName("m_arc_filenameStub")(0).Click  
           ie.document.getElementsByName("m_arc_filenameStub")(0).Value = "myfilenameformat"  
           Found = ClickOn(ie,"OK",0,False,"a")  
      WScript.Sleep 1000  
      Set ie = AttachObject  
      Found = ClickOn(ie,"Uitvoeren",0,False,"a")  
      'Promptscreen  
      WScript.Sleep 1000  
      Set ie = AttachObject  
      Found = ClickOn(ie,"SpecificRadioOptionValue",0,False,"input") 'radio button 
      'Select on class  
      ie.document.getElementsByClassName("clsTextWidget")(1).value = "param1" 
      ie.document.getElementsByClassName("clsTextWidget")(2).value = "param2" 
      Found = ClickOn(ie,"Finish",0,False,"button") 'Finish  
      Do While ie.Busy Or ie.readyState <> 4  
        WScript.Sleep 50  
   Loop  
      msgbox("END = " & ie.LocationURL & "-" & ie.LocationName )  
      ie.Quit  
 End Sub  
 Sub logon()  
      Set ie = CreateObject("InternetExplorer.Application")  
      ie.Visible = True  
      ie.Navigate "http://192.168.1.105:19300/p2pd/servlet/dispatch?b_action=xts.run&m=portal/cc.xts&gohome="  
       Do While ie.Busy Or ie.readyState <> 4  
       WScript.Sleep 50  
     Loop  
      ie.Document.getElementsByClassName("loginButton")(0).click  
       Do While ie.Busy Or ie.readyState <> 4  
       WScript.Sleep 50  
     Loop  
      ie.Document.getElementsByName("CAMUsername")(0).value = "myusername"    
      ie.Document.getElementsByName("CAMPassword")(0).value = "mypassword"   
      ie.Document.getElementsByClassName("loginButton")(0).click  
      Do While ie.Busy Or ie.readyState <> 4  
           WScript.Sleep 50  
      Loop  
      'msgbox(ie.LocationURL & "-" & ie.LocationName)  
      ie.Quit  
 End Sub  

 Sub logoff()  
      Set ie_logoff = CreateObject("InternetExplorer.Application")  
      ie_logoff.Visible = True  
      ie_logoff.Navigate "http://192.168.1.105:19300/p2pd/servlet/dispatch?b_action=xts.run&m=portal/logoff.xts&h_CAM_action=logoff"  
      Do While ie_logoff.Busy Or ie_logoff.readyState <> 4  
           WScript.Sleep 50  
      Loop  
      'msgbox(ie.LocationURL & "-" & ie.LocationName)  
      ie_logoff.Quit  
 End Sub  
 Function AttachObject  
      strPath = "http://192.168.1.105:19300/p2pd/servlet/dispatch"  
      Set oShell = CreateObject("WScript.Shell")  
   Set objShellWindows = CreateObject("Shell.Application").Windows  
   For X = objShellWindows.Count - 1 To 0 Step -1  
     Set oIE = objShellWindows.Item(X)  
     If Not oIE Is Nothing Then  
      ' Uncomment below line to troubleshoot  
       ' MsgBox oIE.LocationName & vbCrLf & oIE.LocationURL  
       'If StrComp(oIE.LocationURL, "file:///" & Replace(strPath, Chr(92), "/", 1, -1, 1), 1) = 0 Then  
                If StrComp(oIE.LocationURL, strPath, 1) = 0 Then  
         Do While oIE.Busy And oIE.ReadyState < 2  
           DoEvents  
         Loop  
         oIE.Visible = 2  
         Exit For  
       End If  
     End If  
     Set oIE = Nothing  
   Next  
   Set objShellWindows = Nothing  
   If oIE Is Nothing Then  
     MsgBox "Could Not Find The IE Window"  
           Set AttachObject = null  
   else  
           Set AttachObject = oIE  
           'msgbox("status = " & oIE.LocationURL & "-" & oIE.LocationName )  
      End If  
 End Function  

 Function ClickOn(oIE,SearchString,index,debugging,tag)       'Index = number of items after found item which will be clicked  
      'msgbox("status = " & ie.LocationURL & "-" & ie.LocationName )  
      set urls = oIE.document.all.tags(tag)  
      For x = 0 to (urls.length)-1  
           if tag = "a" or tag = "button" then  
                If Debugging then  
                     msgbox x & "=" & urls(x).innerHTML & " : " & urls(x).href  
                End if  
                If Instr(urls(x).innerHTML,SearchString) > 0 Then  
                     If Debugging then  
                          msgbox "Found " & x & "=" & urls(x).innerHTML   
                     End if  
                     urls(x+index).Click  
                     ClickOn = True  
                     Exit For  
                else  
                'msgbox x & "van " & urls.length-1 & " " & Trim(urls(x).innerHTML) & " : " & urls(x).href  
                End if  
           Else  
           'for now only "input" items  
                If Debugging then  
                     msgbox x & "=" & urls(x).Value   
                End if  
                if Instr(urls(x).value,SearchString) > 0 Then  
                     If Debugging then  
                          msgbox "Found " & x & "=" & urls(x).innerHTML   
                     End if  
                     urls(x+index).Click  
                     ClickOn = True  
                     Exit For  
                End if  
           End if   
      Next  
 End Function  

As you can see working with the IE object had some downfalls. I had to reattach the object , wait unspecific amount of time to proceed the next step etc, but it actually worked. After installing it on the server in stead of my local machine I learned that the older IE version on the server did not supported some features. This made me pursue other ways of scripting the report.

Attempt 3: PhantomJS to the rescue
PhantomJS  is described as a headless WebKit scriptable with a JavaScript API. It has fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.

After some testing I decided to transform my old script to a PhantomJS script  which workes a lot better

  • the CSS selector makes it much easier to select a element to click on for example
  • the render function allows to make printscreens of the active screen in every step
  • it's possible to run a Javascript function on the page itself
  • PhantomJS can run on any windows machine (independant of the installed IE version)

The script needs to be called with a parameter for example
phantomjs.exe myscriptname.js parameter1


 var page = new WebPage(), testindex = 0, loadInProgress = false;  
 uid = "myusername";  
 pwd = "mypassword";  
 DocumentName = "My documentname";  
 FolderID = "i535F689A7E43421B995CC9B577DA499A"  
 Archive_location = "Facturen Vorming"  
 var system = require('system');  
 if (system.args.length === 1) {  
 console.log('Try to pass some args when invoking this script!');  
 } else {  
 system.args.forEach(function (arg, i) {  
      console.log(i + ': ' + arg);  
      if(i == 1) { Parameter1 = arg;}  
 });  
 }  
 console.log("==========================");  
 console.log("Parameter1 = " + Parameter1);  
 console.log("==========================");  
 //var parameter1 = phantom.args[0];  
 page.onConsoleMessage = function(msg) { console.log(msg);};  
 page.onLoadStarted = function() { loadInProgress = true; console.log("  Laden...");};  
 page.onLoadFinished = function() { loadInProgress = false; console.log("  OK");};  
 var steps = [  
  function() {  
   //Load Login Page  
      console.log("Stap 1 Login pagina");  
      console.log("============================================");  
   page.open("http://192.168.1.105:19300/p2pd/servlet/dispatch?b_action=xts.run&m=portal/cc.xts&gohome=");  
  },  
  function() {  
      console.log("Stap 2 Choose Authentication");  
      console.log("============================================");  
   page.evaluate(function() {  
    var Knop = document.getElementsByClassName("loginButton");  
       Knop[0].click();  
   });  
  },  
  function() {  
      console.log("Stap 3 Enter Credentials :" + uid);  
      console.log("============================================");  
   page.evaluate(function(uid,pwd) {  
           document.querySelector("input[name='CAMUsername']").value = uid;  
           document.querySelector("input[name='CAMPassword']").value = pwd;  
           document.querySelector("#LoginForm").submit();   
   },uid,pwd);  
  },  
  function() { console.log("Stap 4 Printscreen"); page.render('4_Printscreen.png'); },   
  function() {  
      console.log("Stap 5 Open bewuste map");  
      console.log("============================================");  
      page.open("http://192.168.1.105:19300/p2pd/servlet/dispatch?b_action=xts.run&m=portal/cc.xts&m_folder=" + FolderID)  
  },  
  function() {  
      console.log("Stap 6 Klik op Uitvoeren met opties ");  
      console.log("============================================");  
      page.evaluate(function(DocumentName) {  
           var event = document.createEvent( 'MouseEvents' );  
           event.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("img[title='Uitvoeren met opties - " + DocumentName + "']").dispatchEvent( event );  
   },DocumentName);  
  },  
  function() { console.log("Stap 7 Printscreen"); page.render('7_Printscreen.png'); },   
  function() {  
      console.log("Stap 8 Klik op Geavanceerde opties");  
      console.log("============================================");  
      page.evaluate(function() {  
           /*var event = document.createEvent( 'MouseEvents' );  
           event.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("a[href='javascript:advancedOptions()']").dispatchEvent( event );*/  
           advancedOptions();  
      });  
  },  
  function() { console.log("Stap 9 Printscreen"); page.render('9_Printscreen.png'); },   
   function() {  
      console.log("Stap 10 Opties kiezen");  
      console.log("============================================");  
      page.evaluate(function() {  
           var event = document.createEvent( 'MouseEvents' );  
           event.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("input[type='radio'][name='runType'][value='background']").dispatchEvent( event );  
           var event2 = document.createEvent( 'MouseEvents' );  
           event2.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("input[type='checkbox'][name='m_ro_saveOutput']").dispatchEvent( event2 );  
           var event3 = document.createEvent( 'MouseEvents' );  
           event3.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("input[type='checkbox'][name='m_ro_archive']").dispatchEvent( event3 );  
           var event4 = document.createEvent( 'MouseEvents' );  
           event4.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("a[href='javascript:utmlA_d0n47095()']").dispatchEvent( event4 );  
   });  
  },  
  function() { console.log("Stap 11 Printscreen"); page.render('11_Printscreen.png'); },   
  function() {  
      console.log("Stap 12 Enter Filename");  
      console.log("============================================");  
      page.evaluate(function(Parameter1,Archive_location) {  
           var event = document.createEvent( 'MouseEvents' );  
           event.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("input[type='radio'][value='specify_name']").dispatchEvent( event );  
           //Gewenste Filename  
           document.querySelector("input[name='m_arc_filenameStub']").value = "nameconvention-"+Parameter1;  
           // Kies locatie  
           document.querySelector('option[selected]').value = "/configuration/archiveLocation[@name='"+Archive_location+"']";  
           // Behoud bestaande bestanden  
           var event3 = document.createEvent( 'MouseEvents' );  
           event3.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("input[type='radio'][name='m_arc_conflictResolution']").dispatchEvent( event3 );  
           var event4 = document.createEvent( 'MouseEvents' );  
           event4.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           //page.render('14_Printscreen.png');   
           var links = document.getElementsByTagName('a');  
           //for(var l in links) {  
           for (var i = 0; i < links.length; i++) {  
             if (links[i].innerHTML.indexOf('OK') !== -1) {  
                console.log("--found OK button");  
                links[i].dispatchEvent(event4) ;  
                }  
           }  
   },Parameter1,Archive_location);  
  },  
  function() { console.log("Stap 13 Printscreen"); page.render('13_Printscreen.png'); },   
  function() {  
      console.log("Stap 14 Run");  
      console.log("============================================");  
      page.evaluate(function() {  
           var event = document.createEvent( 'MouseEvents' );  
           event.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("a[aria-label='Uitvoeren']").dispatchEvent( event );  
   });  
  },  
  function() { console.log("Stap 15 Printscreen"); page.render('15_Printscreen.png'); },   
  function() {  
      console.log("Stap 16 Parameters");  
      console.log("============================================");  
      page.evaluate(function(Parameter1) {  
           var event = document.createEvent( 'MouseEvents' );  
           event.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           document.querySelector("input[type='radio'][value='Nee']").dispatchEvent( event );  
           document.querySelector("input[type='hidden'][name='p_Myparameter']").value = Parameter1;  
           oCV.promptAction('finish');  
           var event2 = document.createEvent( 'MouseEvents' );  
           event2.initMouseEvent( 'click', true, true, window, 1, 0, 0 );  
           var links = document.getElementsByTagName('button');  
           for (var i = 0; i < links.length; i++) {  
             if (links[i].innerHTML.indexOf('Finish') !== -1) {  
                console.log("--found");  
                links[i].dispatchEvent(event2) ;  
                }  
           }  
           //document.oCV.promptAction('finish');  
   },Parameter1);  
  },  
  function() { console.log("Stap 17 Printscreen"); page.render('17_Printscreen.png'); },   
  function() {  
   page.evaluate(function() {  
           runReport();  
   });  
  },   
  function() {  
   // Output content as image  
   page.render('99_Printscreen.png');  
  }  
 ];  
 interval = setInterval(function() {  
  if (!loadInProgress && typeof steps[testindex] == "function") {  
   console.log("step " + (testindex + 1));  
   steps[testindex]();  
      //console.log("uid= " + uid);  
   testindex++;  
  }  
  if (typeof steps[testindex] != "function") {  
   console.log("test complete!");  
   phantom.exit();  
  }  
 }, 50);