I have a fill-in with the following code, made using the AppBuilder
DEFINE VARIABLE fichNoBuktiTransfer AS CHARACTER FORMAT "N(18)":U
LABEL "No.Bukti Transfer"
VIEW-AS FILL-IN NATIVE
SIZE 37.2 BY 1 NO-UNDO.
Since the format is N, it blocks the user from entering non-alphanumeric entries. However, it does not prevent the user from copypasting such entries into the fill-in. I have an error checking like thusly to prevent such entries using the on leave trigger:
IF LENGTH(SELF:Screen-value) > 18 THEN DO:
SELF:SCREEN-VALUE = ''.
RETURN NO-APPLY.
END.
vch-list = "!,*, ,#,#,$,%,^,&,*,(,),-,+,_,=".
REPEAT vinl-entry = 1 TO NUM-ENTRIES(vch-list):
IF INDEX(SELF:SCREEN-VALUE,ENTRY(vinl-entry,vch-list) ) > 0 THEN DO:
SELF:SCREEN-VALUE = ''.
RETURN NO-APPLY.
END.
END.
However, after the error handling kicked in, when the user inputs any string and triggers on leave, error 632 occurs:
error 632 occurs
Is there any way to disable the error message? Or should I approach the error handling in a different way?
EDIT: Forgot to mention, I am running on Openedge version 10.2B
You didn't mention the version, but I'll assume you have a version in which the CLIPBOARD system handle already exists.
I've simulated your program and I believe it shouldn't behave that way. It seems to me the error flag is raised anyway. My guess is even though those symbols can't be displayed, they are assigned to the screen value somehow.
Conjectures put aside, I've managed to suppress it by adding the following code:
ON CTRL-V OF FILL-IN-1 IN FRAME DEFAULT-FRAME
DO:
if index(clipboard:value, vch-list) > 0 then
return no-apply.
END.
Of course this means vch-list can't be scoped to your trigger anymore, in case it is, because you'll need the value before the leave. So I assigned the special characters list as an INIT value to the variable.
After doing this, I didn't get the error anymore. Hope it helps.
To track changes in a fill-in I always use at first this code:
ON VALUE-CHANGED OF FILL-IN-1 IN FRAME DEFAULT-FRAME
DO:
/* proofing part */
if ( index( clipboard:value, vch-list ) > 0 ) then do:
return no-apply.
end.
END.
You could add some mouse or developer events via AppBuilder to track changes in a fill-in.
Related
Anybody have any tips for looping, and continue? For example, I placed about 2500 pnrs on a queue, and I need to add a remark to each of them. Is it possible for a script to add the remark then move to the next pnr?
For example, I placed about 2500 pnrs on a queue, and I need to add a remark to each of them. Is it possible for a script to add the remark then move to the next pnr?
Loops are supported in Scribe but have to be built manually by creating your own iteration variables and breaking the loop manually when you know the work is complete.
What you are describing is definitely possible, but working in queues can be difficult as there are many possible responses when you try to end the PNRs. You'll have to capture the response to detect whether you need to do something else to get out of the error condition (e.g. if a PNR warning indicates you have to double-end the record).
If possible, its likely simpler to work off the queue by collecting all PNR locators and then looping through that list, adding your remarks, and then ending the PNRs. You'll still have to capture the response to determine if the PNR is actually ended properly, but you won't have to deal with the buggy queue behavior. A basic Scribe loop sample is below. Note that I haven't been a Scribe developer for a while and I did this in Notepad so there might be some errors in here, but hopefully it's a good starting point.
DEFINE [ROW=N:8] ;iteration variable/counter
DEFINE [LOCATOR_FILE=*:60] ;File Path
DEFINE [TEMP_LOCATOR=*:6] ;pnr locator variable, read from the temp file
DEFINE [BREAK=*:1] ;loop breaking variable
OPEN F=[TEMP_LOCATOR] L=0 ;open the file of locators
[BREAK] = ""
[ROW] = 0
REPEAT
[ROW] = [ROW] + 1
[TEMP_LOCATOR] = "" ;Reset temp locator variable, this will break our loop
READ F=[LOCATOR_FILE] R=[ROW] C=1 [TEMP_LOCATOR]
IF $[TEMP_LOCATOR] = 6 THEN ;test length of locator, if this is 6 chars, you have a good one, pull it up and add your remark
»"5YOUR REMARK HERE"{ENTER}«
»ER{ENTER}«
;trap errors
READ F="EMUFIND:" R=0 C=0 [TEMP_LOCATOR] ;read for the locator being present on this screen, which should indicate that the ER was successful - you'll have to trap other errors here though
IF [#SYSTEM_ERROR] = 0 THEN ;this locator was found, ER appears successful
»I{ENTER}« ;Ignore this PNR and move to the next one
ELSE
[BREAK] = "Y" ;error found afeter ER, break loop. Maybe show a popup box or something, up to you
ENDIF
ELSE ;No locator found in file, break the loop
[BREAK] = "Y"
ENDIF
UNTIL [BREAK] = "Y"
CLOSE [LOCATOR_FILE]
I wrote a query which contains multiple for each statements. The query is taking more than 20 minutes to fetch the data. Is there a way to check what time each loop started and ended. (How much time does each loop takes to execute and also the total time taken to complete the program).
You could do as you ask (just follow JensD's suggestsions) but you would likely be better served to use the profiler. You can easily add profiling for a code snippet:
assign
profiler:enabled = yes
profiler:description = "description of this test"
profiler:profiling = yes
profiler:file-name = "filename.prf"
.
/* this is deliberately awful code that should take a long time to run */
for each orderline no-lock:
for each order no-lock:
for each customer no-lock:
if customer.custNum = order.custNum and orderLine.orderNum = orderLine.orderNum then
. /* do something */
end.
end.
end.
/* end of test snippet */
assign
profiler:enabled = no
profiler:profiling = no
.
profiler:write-data().
You can then load that prf file into an analysis tool. The specifics depend on your development environment - if you are using an up to date version of PSDOE there is a Profiler analyzer included, if not you might want to download ProTop
https://demo.wss.com/download.php and use the simple report included in lib/zprof_topx.p.
Ultimately what you are going to discover is that one or more of your FOR EACH statements is almost certainly using a WHERE clause that is a poor match for your available indexes.
To fix that you will need to determine which indexes are actually being selected and review the index selection rules. Some excellent material on that topic can be found here: http://pugchallenge.org/downloads2019/303_FindingData.pdf
If you don't want to go to the trouble of reading that then you should at least take a look at the actual index selection as shown by:
compile program.p xref program.xref
Do the selected indexes match your expectation? Did WHOLE-INDEX (aka "table scan") show up?
Using ETIME you can initiate a counter of milliseconds. It could be called once or several times to tell how much time has passed since reset.
ETIME(TRUE).
/*
Loop is here but instead I'll insert a small pause.
*/
PAUSE 0.5.
MESSAGE "This took" ETIME "milliseconds" VIEW-AS ALERT-BOX.
Milliseconds might not be useful when dealing with several minutes. Then you can use TIME to keep track of seconds but you need to handle start time yourself then.
DEFINE VARIABLE iStart AS INTEGER NO-UNDO.
iStart = TIME.
/*
Loop is here but instead I'll insert a slightly longer pause.
*/
PAUSE 2.
MESSAGE "This took" TIME - iStart "seconds" VIEW-AS ALERT-BOX.
If you want to keep track of several times then it might be better to output to a log file instead of using a MESSAGE-box that will stop execution until it's clicked.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DEFINE STREAM str.
OUTPUT STREAM str TO c:\temp\timing.txt.
ETIME(TRUE).
/*
Fake loop
*/
DO i = 1 TO 20:
PAUSE 0.1.
PUT STREAM str UNFORMATTED "Timing no " i " " ETIME "ms" SKIP.
END.
OUTPUT CLOSE.
I have a text file that contains some basic passwords and some variants of those basic passwords. They are typed out together like this:
qwerty, qwerty1
password, password1
default, default 1
123, 12345, 123456
I am trying to take these values and split them, storing them in a tuple and then print out the values as 'Password' and then any variants, but I am getting a syntax error on the print BIF? (I am aware this will not print out the password '123456', I am just trying to solve the syntax error first.)
for each_line in passwords:
(passwd, passwd_variant) = each_line.split(',',1)
print(f'Password: {passwd}, Variant {passwd_variant}')
SyntaxError: invalid syntax
normally if I was writing a script, in the editor I would put print on a new line, however a new line in IDLE simply executes the code above it. is there a shortcut or something to do a carriage return and write the print statement on a new line and if so, is that the cause of the syntax error and why?
Cheers
EDIT: I would like my output to be this
Finally discovered that you need to hit Enter after the for in statement, this will then take you to a new line inside for in statement. Was up all night trying to work it out, maybe my fresh brain helped this morning.
I am searching for a way to get user input inside a loop while executing in batch mode.
readLines() and scan() work well for me in interactive mode only, in batch mode they start to read in lines of code as user input, unless all the code is surrounded by {}, which is inconvenient. I need a simple solution to get just 1 integer value in a way that I can just type in value and press ENTER, so
the input field (if the solution involves GUI) must automatically get focus and
ENTER must trigger end of input/submission.
I can't find a way to do it that will satisfy both conditions, e.g. ginput() from gWidgets activates the input field, but ENTER doesn't trigger form submission.
Here is how I solved my own problem:
require(gWidgets)
options(guiToolkit="RGtk2")
INPUT <- function(message) {
CHOICE <- NA
w <- gbasicdialog(title=message, handler = function(h,...) CHOICE <<- svalue(input))
input <- gedit("", initial.msg="", cont=w, width=10)
addHandlerChanged(input, handler=function (h, ...) {
CHOICE <<- svalue(input)
dispose(w)
})
visible(w, set=TRUE)
return(CHOICE)
}
repeat{
x=INPUT("Input an integer")
if(!is.na(as.integer(x))) break
}
print(x)
Update:
I can't test this right now, but take a look at ?menu and have it pop up a gui window.
I'm not certain if that will work, but it is different in that it takes a mouse-click response.
original answer:
As per the documentation to ?readline:
This can only be used in an interactive session.
..
In non-interactive use the result is as if the response was RETURN and the value is "".
If you are simply waiting for one piece of information, and you do not know this piece of information before beginning the execution of the script (presumably, there is a decision to be made which is dependent on the results earlier in the script), then one alternative is to simply break your script up into three parts:
everything before the decision point.
an interactive script which prompts for input
everything after the decision point.
And simply chain the three together by having the first end by calling the second in an interactive session. Then have the second end by calling the third.
(first question here, sorry if I am breaking a piece of etiquette)
My site is running on an eCommerce back end provider that I subscribe to. They have everything in classic ASP. They have a black box function called import_products that I use to import a given text file into my site's database.
The problem is that if I call the function more than once, something breaks. Here is my example code:
for blah = 1 to 20
thisfilename = "fullcatalog_" & blah & ".csv"
Response.Write thisfilename & "<br>"
Response.Flush
Call Import_Products(3,thisfilename,1)
Next
Response.End
The first execution of the Import_Products function works fine. The second time I get:
Microsoft VBScript runtime error '800a0009'
Subscript out of range: 'i'
The filenames all exist. That part is fine. There are no bugs in my calling code. I have tried checking the value of "i" before each execution. The first time the value is blank, and before the second execution the value is "2". So I tried setting it to null during each loop iteration, but that didn't change the results at all.
I assume that the function is setting a variable or opening a connection during its execution, but not cleaning it up, and then not expecting it to already be set the second time. Is there any way to find out what this would be? Or somehow reset the condition back to nothing so that the function will be 'fresh'?
The function is in an unreadable include file so I can't see the code. Obviously a better solution would be to go with the company support, and I have a ticket it in with them, but it is like pulling teeth to get them to even acknowledge that there is a problem. Let alone solve it.
Thanks!
EDIT: Here is a further simplified example of calling the function. The first call works. The second call fails with the same error as above.
thisfilename = "fullcatalog_testfile.csv"
Call Import_Products(3,thisfilename,1)
Call Import_Products(3,thisfilename,1)
Response.End
The likely cause of the error are the two numeric parameters for the Import_Products subroutine.
Import_Products(???, FileName, ???)
The values are 3 and 1 in your example but you never explain what they do or what they are documented to do.
EDIT Since correcting the vender subroutine is impossible, but it always works for the first time it's called lets use an HTTP REDIRECT instead of a FOR LOOP so that it technically only gets called once per page execution.
www.mysite.tld/import.asp?current=1&end=20
curr = CInt(Request.QueryString("current"))
end = CInt(Request.QueryString("end"))
If curr <= end Then
thisfilename = "fullcatalog_" & curr & ".csv"
Call Import_Products(3,thisfilename,1)
Response.Redirect("www.mysite.tld/import.asp?current=" & (curr + 1) & "&end=" & end)
End If
note the above was written inside my browser and is untested so syntax errors may exist.