How to export data from browser widget to CSV in Progress 4Gl - progress-4gl

Did anyone tried to export data directly from browser to CSV? We are populating data to browser which has complex logic. If i can get data directly from browser to CSV that would be helpful.
Thanks,
MSK

Are you referring to a browse widget? A browse widget visualizes the data in an underlying query -- the query result set would be what you want to export. There is no EXPORT method for a query so, unfortunately you will need to roll your own. Something like this (many details omitted):
function exportData returns logical ( input bh as handle ):
define variable bf as handle no-undo. /* handle to the field */
define variable f as integer no-undo. /* field number */
define variable i as integer no-undo. /* array index */
do f = 1 to bh:num-fields: /* for each field... */
bf = bh:buffer-field( f ). /* get a pointer to the field */
if f > 1 then put stream expFile unformatted field_sep. /* output field separator */
if bf:extent = 0 then /* is it an array? */
put stream expFile unformatted
( if bf:data-type = "character" then /* character data needs to be quoted to */
quoter( string( bf:buffer-value )) /* handle (potential) embedded delimiters */
else /* and quotes within quotes! */
string( bf:buffer-value ) /* other data types should not be quoted */
)
.
else /* array fields need special handling */
do i = 1 to bf:extent: /* each extent is exported individually */
if i > 1 then put stream expFile unformatted field_sep. /* and a field separator */
put stream expFile unformatted
( if bf:data-type = "character" then /* see above... */
quoter( string( bf:buffer-value( i )))
else
string( bf:buffer-value( i ))
)
field_sep
.
end.
end.
put stream expFile skip. /* don't forget the newline! ;-) */
return true.
end.
/* main block...
*/
/* skipping a few things like defining variables and setting up the query that you have, presumably, already done... */
output to "output.dat".
qh:get-first( no-lock ) no-error. /* try to fetch the first record */
do while ( qh:query-off-end = no ): /* loop while there is data being fetched */
exportData( bh ). /* write it to the output file! */
assign /* track the number of records written... */
r = r + 1 /* per table */
.
qh:get-next( no-lock ) no-error. /* fetch the next record */
end.

Related

4GL ABL Openedge loop through handle?

here is my current code
def var hbTT as handle.
for each Cust:
hbTT:buffer-create().
assign
hbTT::Name = Cust.Name
hbTT::address = Cust.Address.
end.
now what I want to do is to loop through hbtt. How can I do that?
I tried
for each hbTT:
/* Do something */
end.
the error I get is
unknown or ambiguous table hbTT. (725)
thank you
You won't be able to do a loop that way, as for each requires a static name.
Instead, try this:
DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
create query hQuery.
hQuery:set-buffers(hbtt).
hquery:query-prepare('for each tt'). /* <-- Where tt is the original buffer name */
hquery:query-open().
hquery:get-first().
do while not hquery:query-off-end:
disp hbtt::name hbtt::address .
hquery:get-next().
end.

How to get the field name dynamically and update it to main table in progress

Program:It is a just maintenance program, in this one it displays the Item Code in one frame and it prompt for the input. if you enter the item code it has to displays what are the blank fields for that record in pt_mstr and display in one frame(No need to display all blank fields, just first 4 or 5 fields enough). and also in that frame only if user want to update it update directly to main table pt_mstr.
What i tried is, i just write the code for getting blank fields using buffer handle and after that i create one temp table and displaying the fields, i strucked there itself, i am unable to update fields.
My code:
/*Sample Item master Maintenance Program*/
/* DISPLAY TITLE */
{us/mf/mfdtitle.i "3+ "}
DEFINE VARIABLE hBuffer AS HANDLE NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DEFINE VARIABLE j AS INTEGER NO-UNDO.
DEFINE VARIABLE hField AS HANDLE NO-UNDO.
define variable fldnm as character extent 10 no-undo.
define temp-table tt_temp no-undo
field tt_part like pt_part
field field1 as char extent 10.
form
pt_part colon 25
with frame a side-labels width 80.
setFrameLabels(frame a:handle).
/* DISPLAY */
view frame a.
repeat with frame a:
prompt-for pt_part
editing:
/* FIND NEXT/PREVIOUS RECORD */
{us/mf/mfnp.i pt_mstr pt_part "pt_mstr.pt_domain = global_domain and pt_part" pt_part pt_part pt_part }
if recno <> ? then
do:
display pt_part.
find pt_mstr where pt_part = input pt_part and pt_domain=global_domain no-lock no-error.
ASSIGN hBuffer = BUFFER pt_mstr:HANDLE.
empty temp-table tt_temp.
j = 1.
DO i = 1 TO 10:
ASSIGN hField = hBuffer:BUFFER-FIELD(i).
IF ((hField:BUFFER-VALUE = "" )) THEN
do:
/* message hField:NAME "test" view-as alert-box.*/
find first tt_temp where tt_part = pt_part no-lock no-error.
if not avail tt_temp then
do:
create tt_temp.
assign
tt_part = pt_part
field1[j] = hField:NAME.
j = j + 1.
end.
else do:
assign
field1[j] = hField:NAME.
j = j + 1.
end.
end.
end.
end.
for each tt_temp:
display field1[1] field1[2] field1[3] field1[4].
end.
end.
end.
Are you sure you need your temp-tables to do this? I've created an example using only the actual table (but created a fake temp-table instead). You would have to look into data error handling, data validation, transaction, locking etc before putting this into production of course.
/*First we need some fake data */
DEFINE TEMP-TABLE ttMockedData NO-UNDO
FIELD id AS INTEGER
FIELD dataName AS CHARACTER FORMAT "x(8)"
FIELD dataType AS CHARACTER FORMAT "x(8)"
FIELD dataDescrioption AS CHARACTER FORMAT "x(32)".
DEFINE VARIABLE iId AS INTEGER NO-UNDO.
DEFINE VARIABLE iSearch AS INTEGER NO-UNDO LABEL "Search".
PROCEDURE createData:
DEFINE INPUT PARAMETER pcName AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcType AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcDesc AS CHARACTER NO-UNDO.
iId = iId + 1.
CREATE ttMockedData.
ASSIGN
ttMockedData.id = iId
ttMockedData.dataName = pcName
ttMockedData.dataType = pcType
ttMockedData.dataDesc = pcDesc.
END PROCEDURE.
RUN createData("Test 1", "TESTTYPE", "A TEST").
RUN createData("Test 2", "", "ANOTHER TEST").
RUN createData("", "TESTTYPE 2", "").
RUN createData("4", "", "").
/* Program starts here */
updating:
REPEAT:
UPDATE iSearch WITH FRAME x0.
IF iSearch > 0 THEN DO:
FIND FIRST ttMockedData NO-LOCK WHERE ttMockedData.id = iSearch NO-ERROR.
IF NOT AVAILABLE ttMockedData THEN DO:
MESSAGE "Not found" VIEW-AS ALERT-BOX ERROR.
RETURN ERROR.
END.
ELSE DO:
DISP ttMockedData WITH FRAME x1 1 COLUMNS SIDE-LABELS.
/* Is there an empty field? - Then we update! */
IF ttMockedData.dataName = ""
OR ttMockedData.dataType = ""
OR ttMockedData.dataDescrioption = "" THEN DO:
DISPLAY
ttMockedData.dataName
ttMockedData.DataType
ttMockedData.dataDesc
WITH FRAME x2 1 COLUMN SIDE-LABELS TITLE "Complete the data...".
/* This isn't working with temp-tables of course! */
/* Just here to make sure you handle locking! */
FIND CURRENT ttMockedData EXCLUSIVE-LOCK.
UPDATE
ttMockedData.dataName WHEN ttMockedData.dataName = ""
ttMockedData.DataType WHEN ttMockedData.DataType = ""
ttMockedData.dataDesc WHEN ttMockedData.dataDesc = ""
WITH FRAME x2.
/* This isn't working with temp-tables of course! */
/* Just here to make sure you handle locking! */
FIND CURRENT ttMockedData NO-LOCK.
END.
END.
END.
ELSE LEAVE updating.
END.

Bracketed key value - limit OR foreach ... in OR?

Continuing my quest to convert .NET to Progress, I faced another challenge yesterday.
Our company bought time ago a .NET DLL to manage Excel document without the need to install Microsoft Excel. There is several functions that return a series of cells depending of the need.
The returned value is a class that implement IEnumerator interface in .NET.
The problem is that I cannot find a way to iterate trough the cells without getting the error:
System.ArgumentException: Row or column index is invalid or out of required range
Is there a way to in Progress to validate if X is inside of the extent range?
OR
Is there a way to iterate trough the array without knowing the upper limit of the array?
Thank you!
Sebastien
--- temporary solution ---
/* declaration */
DEFINE VARIABLE oCell AS CLASS GemBox.Spreadsheet.ExcelCell NO-UNDO.
DEFINE VARIABLE oRange AS CLASS GemBox.Spreadsheet.CellRange NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
/* load excel file */
...
/* retrieve a series of cells */
ASSIGN oRange = oWorksheet:Cells:GetSubrangeAbsolute(1,1, 2,2).
/* first cell */
ASSIGN i = 0.
ASSIGN oCell = ?.
ASSIGN oCell = oRange:Item[i] NO-ERROR.
/* validate cell is in the range */
DO WHILE NOT oCell EQ ?:
MESSAGE oCell:Value VIEW-AS ALERT-BOX.
/* next cell */
ASSIGN i = i + 1.
ASSIGN oCell = ?.
ASSIGN oCell = oRange:Item[i] NO-ERROR.
END.
I don't have access nor I can test this solution, but if it implements correctly the interface some solution like this one should work:
/* declaration */
DEFINE VARIABLE oCell AS CLASS GemBox.Spreadsheet.ExcelCell NO-UNDO.
DEFINE VARIABLE oRange AS CLASS GemBox.Spreadsheet.CellRange NO-UNDO.
DEFINE VARIABLE oEnumerator AS CLASS System.Collections.IEnumerator NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
/* load excel file */
...
/* retrieve a series of cells */
ASSIGN oRange = oWorksheet:Cells:GetSubrangeAbsolute(1,1, 2,2).
oEnumerator = oRange:getEnumerator().
DO WHILE oEnumerator:MoveNext():
oCell = CAST(oEnumerator:current,"GemBox.Spreadsheet.ExcelCell").
END.
If it doesn't work exactly like this, at least it should point you in the correct direction to use it.
From the web page I'd infer that the # of cols =
oRange:LastColumnIndex - oRange:FirstColumnIndex
and the # of rows is
oRange:LastRowIndex - oRange:FirstRowIndex
I'd think using
oCell = oRange:Item[Int32, Int32]
to get the item at the row, col position would work better instead of using a single element array element.

Usage of LOB's in progress 4GL

As known Progress provides 4 large object datatypes data types MEMPTR,CLOB,BLOB,LONGCHAR.
But the string functions Can't be used either on CLOB or LONGCHAR datatypes.
How to use perform string operations on these LOB Datatypes.
"string operations" means substring ,replace,trim etc. functions which can be performed on the strings.To be more clear
Define vChar as Character INITIAL "ashdbi" NO_UNDO.
MESSAGE SUBSTRING(vChar,1,1)
VIEW-AS ALERT_BOX .
The same way can we perform string operations on LOB's?
A CLOB is stored in the database and LONGCHAR is the datatype used to manipulate it locally. If you store a BLOB you must use a MEMPTR if you want to handle it locally.
Since your asking about STRING-related functions I assume that CLOBS and LONGCHARs are what you're after (CLOB = Chararcter Large Object as supposed to BLOB = Binary Large Object).
Several (or some) string manipulation methods and functions can be used on LONGCHARS, for instance SUBSTRING. Regardless if you're using a CHARACTER or LONGCHAR the syntax is displayed. If you want to
Example - SUBSTRING, INDEX and COPY-LOB
DEFINE VARIABLE cStart AS LONGCHAR NO-UNDO.
DEFINE VARIABLE cEnd AS LONGCHAR NO-UNDO.
DEFINE VARIABLE cString AS CHARACTER NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
/* Fill the variable with lots of bogus data */
DO i = 1 TO 10000:
cStart = cStart + "ABCDEFGHIJKLMN".
/* Insert a _ once in 100 */
IF RANDOM(1, 100) = 100 THEN cStart = cStart + "_".
END.
DISPLAY LENGTH(cStart).
/* SUBSTRING */
cEnd = SUBSTRING(cStart, 1, 100000).
DISPLAY LENGTH(cEnd).
/* Is there a _ in the string - most likely! */
DISPLAY INDEX(cStart, "_").
/* SUBSTRING will convert output to CHARACTER if the length is less than roughly 32k */
cString = SUBSTRING(cStart, 1, 30000).
DISPLAY cString.
/* Lets save the CLOB so we can look at it */
COPY-LOB FROM cStart TO FILE "c:\temp\testing.txt".
/* Actually you can DISPLAY a LONGCHAR as well but why would you really? */
DISPLAY cStart
VIEW-AS EDITOR LARGE INNER-LINES 300 INNER-CHARS 300
WITH FRAME x1 WIDTH 320 .
There were some exceptions in older releases but "string functions" work very well on longchar data.
To get data between the various large objects (CLOB, BLOB, MEMPTR and files) and into a LONGCHAR and vice-verse you need to use the COPY-LOB statement. Is suspect that that is the "secret sauce" that you are missing.
For instance:
define variable cfgData as longchar no-undo.
assign file-info:file-name = search( "etc/stomp.cfg" ).
copy-lob from file file-info:full-pathname to cfgData no-error.
stompCfg = new dotr.Stomp.StompConfig().
assign
stompCfg:StompPort = "61613"
stompCfg:StompServer = entry( 1, cfgData, ":" )
stompCfg:StompPort = entry( 2, cfgData, ":" ) when num-entries( cfgData, ":" ) > 1
stompCfg:LargeMessageSupport = yes
.

RC special variable in REXX?

how to assign a value to RC special variable in REXX?
/* REXX */
"LISTDS ?" /* Command that sets RC to 12 */
SAY 'RC IS' RC /* RC is 12 */
RC = X /* RC set to X */
SAY 'RC IS' RC /* RC is X */
The above works, there is nothing special about the RC variable except it will be over written by the return code from the last command.
So you can set it to whatever you want at least on a mainframe running Zos.
Maybe you need to provide more detail in your question like what type of Rexx it is (Classic or OO) and what environment you are using.
If you want to set the return value of your method you need to use the "return" commend and to get the return code with the "result", for example:
/* REXX - program A */
SAY "THIS IS PROG. A WITH RC = 4"
RETURN 4
/* REXX - PROGRAM B */
SAY "CALLING PROGRAM A..."
CALL PROG_A
RC = RESULT
SAY "RC = "RC " RETURN FROM PROGRAM A..."
As Deuian said before, RC is set by last command executed and more detail should be provided to get a precise answer (environment, goal/task, batch/interactive etc.).
A silly working way to set RC on Zos REXX is to make a buffer: RC is set to the buffer count (so if you need RC = 100 you should create 100 buffers...), see the example (I do not endorse the usage of this method, it's just a conjecture)
/* rexx */
'MAKEBUF'
say RC
'MAKEBUF'
say RC
'DROPBUF'
say RC
/* exec output */
1
2
0
***
Beware that the previous code leaves a buffer active! (another DROPBUF needed)
The SAY instructions will send screen prompts or include text in the output. If you want to set the RC to something that can be interpreted by subsequent steps in a job, try:
/* REXX */
setrc = X /* set a variable for RC to X */
exit(setrc)