Using Ensemble 2018.1.2, I'm using the pInput.Attributes("FTPDir") to get the full difectory of the filename (where pInput is %Stream.Object) when I have a root directory defined (e.g. /send) and the property Subdirectory Levels = 2.

I also, was trying to find a way  to use the subfolders of the file that's on the server to either run a rule or more simply, create the same folders on our side.  When the production service uses Subdirectory Levels, this subfolder information is not part of the ..Adapter.FilePath (e.g. the root folder is /send, the file is /send/sub1/sub2/testfile.txt, Subdirectory Levels=2, but the Adapter.FilePath = /send/testfile.txt)

However, using the FTPDir attribute of the %Stream.Object gives me the full directory of the source object, from which I can "piece" together appropriate storage path on our end.


Hi Keith, I am sorry! In a SQL database (MySQL, Oracle...), is there a way to drop a table (not a view; a Table) without deleting data?

If you really want to remove the SQL table but keep the data, you can merge the global that stores the actual data into a diffefrent global, such as merge ^SavedData = ^Package.TablenameD (You have to find the right global name from the Storage in the class).

This could take a few minutes to complete. Then you can delete the class.  I don't recommend this.

I suggest you contact IS to help you create a table definition that can be indexed, and perhaps populate this new table with your current data - perhaps a new table will be more efficient. IS is very helpful when it comes to support. And, let us know how it goes!



Definitely remove the MAXLEN! I had copied code from my own page, and changed the name of the variable, but not its MAXLEN property. An aside: note that the efault MAXLEN is 50, which I run up against, a lot.   

And do read the documentation that Jean sent out.  I had to read the documentation many times to get it all, and I still go back to it.

Arnold, it could be a scope problem / zen expression problem.

Try something like this:

Property currentTableRow As %ZEN.Datatype.string(MAXLEN = 5000);

Method MyMethodOnServer() [ZenMethod] {

set %page.currentTableRow="1"


I had trouble with a longer string. For a string that's not a number, try this:

set %page.currentTableRow="line 1"

set str=..QuoteJS(%page.currentTableRow)


Hey Keith, how's it going from this time last year?  Caché was my first language (actually, it was MUMPS then), and I've always worked with data stored in globals.  When I learned SQL, MySQL, and Java, I realized that I could probably never go to a language where I coudln't "see" the actual data.   Having to rely on tables and SQL and not be able to get to the actual data stored on disk? I'd feel  like I did when I was using chopsticks for the first time.  

When I tell other non-InterSystems programmers how we have access to the actual data, they're flabbergasted and amazed. But, that is the key to understanding the Storage in each class (which by now, you're probably quite familiar with).   SQL will read this Storage "map" which maps the properties to the global storage, and picks it out of the global for us.  

Back in the old days, we would create our own global structure to hold the data.  Globals often looked like ^Point(pointId,"X")=3  and ^Point(pointId,"Y")=4, or more often, ^Reg(PatientId,"Name")="Smith,Amy", or ^Patient(PatientId,1)=Name^age^DOB^ZipCode and ^PatientIndex("Smith,Amy",PatientId)="" and ^Patient("Count")=last PatientId

Any IDX programmers here?  We had a entire book just to understand the globals.

These days, we let InterSystems create the global structure for us, which appears in the Storage definition when a class is compiled, but it's still the same as before -- the data is still stored in a global, and you can edit it and delete it from the global - not recommended though because IS keeps track of counters.

Anyway, I'm sure you're all set, but I thought it might help someone reading this to know the history of data storage and how it's a completely different paradigm than other databases. 

Also, I was looking for the best way to delete the storage from a deleted property.  You know - you delete the property, but don't delete the storage element, and the node is still there in the global?  Normally, this wouldn't ever be noticed, because when using SQL, you don't see the deleted property, but in our case, we are using the global's stucture to view certain structures in the data (it's a bunch of arrays of Objects).  The deleted property's storage was getting in our way.

I decided the best way to delete the leftover data was to simply kill the nodes: ^Test.Package.ClassNameD(id,"TestProperty")=""

This would have been impossible with SQL because the property doesn't exist anymore!



Hi Arnold,

Regarding you error, looks like Cristiano is correct when he said that %DrawHTML writes only to a device, which is what I am doing (%File) so that works for me.  Drawing it to a string won't work. A very roundabout way would be to write the HTML to a file, and then read the file into the body of an email ... 

As for getRowData(), you'd want to start in a ClientMethod (js) and loop through the table's rows, getting the data for each row, and sending that to the server, perhaps to be stored in a global until you're done? Perhaps passing a large JSON would be better, but I don't have that experience. When you're done on the client, you'd go back to the server, loop through your global, and build your email body.  However, I know from experience that getRowData() is very slow, and should be used to get only the 1 row that you need -- it's not good for looping through to get the all of the data.

In that case, perhaps you should revisit Crisitano's suggestion of just running the table's query again, and using the resulting ResultSet to get and print out the data in a pretty HTML string.  I haven't used %DisplayFormatted, but that might have lots of options for you.

Hi Vivek,

We bought a license for CoolUtils (coolutils.com), whch also has  command line  functionality.  There are dozens of different converters with CoolUtils, but you can buy just the one.

We use a batch file for the command line, which will take two parameters, and an "option file" (used by the converter):

"C:\Program Files (x86)\TotalHTMLConverter\HTMLConverter.exe" "%1" "%2" -c pdf -fp -combine -si -pc Normal -ps letter -OptionFile %3

then call the batch file using $zf:

set command=dir_"Converters\html\ConvertHtmlToPDF.BAT"
set params(1)=fromHTMLfilename

set params(2)=toPDFfilename

set params(3)=OptionFileName

; Fire off HTML>PDF Converter program in background (-2 = don't wait)


The OptionFile has PDF codes like:

PgHead=&b&d &t
PgFoot=&bPage &p of &P&bInsights powered by RevenueHealth Systems (C) 2018

I used the utility's "Command Line Parameters" help file to figure out how to set all the options, and how to use the option file.  I wrote a note to myself to "See http://www.verydoc.com/htmlprint-footer-header.html" for use on the pdf codes that are in the option file. 

It took a while, and I had to program in a new "language" of PDF options and stuff.

Good luck!

Hi Arnold,

I do exactly that with the built in tablePane method %DrawHTML()

My method uses code like this:

//get tablePane component - copy to new object? (pointers, etc.)
#dim tblPane as %ZEN.Component.tablePane
set tblPane=%page.%GetComponentById("tblPaneData")
// you have access to the columns here, if you need to change things up
set numCols = tblPane.columns.Size
set lastCol = tblPane.columns.GetAt(numCols)
set hidden = lastCol.hidden
set lastCol.hidden=1

// I'm writing this to a file, for use in a moment...
$$$ThrowOnError(File.Open("WNS",10)) Use File.Name

//Draw HTML headers - Note: HTML is not my strong point
"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 transitional//EN"">",!

//custom style just for this table (see elements in browser debugger! )

//I must have looked at the tablePane in the browser debugger
  W "h1,h2 {",!
  W "font-size: 20px;",!
  W "font-weight:bold;",!
  W "font-family: verdana;",!
  W "text-align: left;",!
// etc. etc.

 W "<meta http-equiv=""Content-Type"" content=""text/css"" charset=""UTF-8"">",!

//Write specific table header
set header=$g(%session.Data("Heading One Here"))
"<h2>Heading Two Here</h2>",!

//write tablepane element to file
do tblPane.%DrawHTML()

//Finish HTML
" </body>",!

//Close File
do File.Close()


Perhaps you can return  string of HTML for your email. We use the html  converted to PDF using a third party converter  tool.  This lets the user print the table.

I hope this helps!

Hi Peter and Jude,

I am also trying to use the idea of INTERSECT. Instead of intersect, I'm using a RIGHT JOIN or a LEFT JOIN, but the query plan isn't using the faster table to loop through the data.

I found that changing the Group By to the table that I want the query plan to use for the loop, made it much faster.

Just a note: I haven't had time to log the $s values yet, but I'm thinking that's the way to go to track down the "leak".  I did add the %UnSwizzle, but it didn't help enough. When I point the clone's Youngest property to a new Child, the previous youngest Child still exists in memory, and I feel like that is a problem.

I'll update this thread when I get a chance to test.

Thanks for the help,