SAS - Reading a specific file from a FTP location - ftp

I am able to read my files from the FTP location if I specify the exact filename. My problem is that I'm trying to automate this process where I have to read these files week over week and the filename changes randomly. There is no specific pattern to it, so it can't be predetermined.
Is there a way in SAS where I can read the name of all the files present at an FTP location and give the user a dialog box with this information, for them to enter the filename they want to read.

In the SAS Display Manager interface you can use a data step WINDOW or macro %WINDOW statements to define a simple picker, and DISPLAY or %DISPLAY to raise it. The simple picker is really simple, no scrollers or other modern adornments.
An FTP folder listing is retrieved using the filename FTP engine option LS
Sample code:
/**/
* location of FTP folder;
filename folder ftp
user = 'anonymous'
host = 'ftp.cdc.gov'
cd = '/pub/Health_Statistics/NCHS/Publications/ICD9-CM/2011'
ls
;
* retrieve listing;
data files;
infile folder;
input;
order + 1;
fileinfo = _infile_;
run;
/**/
%macro picker(
/* Dynamically build a %WINDOW definition, display it and return the last selected item */
name=, /* Name of window */
title=, /* First line text */
data=, /* data set containing items */
order=order, /* variable for ordering items in the picker*/
item=, /* variable to pick a value of */
result= /* name of macro variable that will contain the picked item, must pre-exist in caller scope */
);
%* field definitions will look like
%* #2 #2 field<i> 1 color=blue attr=rev_video " &filename<i>" ;
%local n i row field_def;
proc sql noprint;
select count(*) into :n trimmed from &data;
%do i = 1 %to &n;
%local field&i item&i;
%end;
select
&order, &item
into
:order1-, :item1-
from
&data;
quit;
%do i = 1 %to &N;
%let field_def = &field_def #%eval(&i+1) #2 field&i 1 color=blue attr=rev_video " &&item&i";
%end;
%WINDOW PICKER rows=30 columns=80
#1 #1 "&title. - Mark an item and Press F3"
&field_def
;
%display PICKER;
%do i = 1 %to &N;
%if %length(&&field&i) %then %let &result=&&item&i;
%end;
%mend;
%let selected=;
%picker(name=PICKER, title=Pick a file, data=files, item=fileinfo, result=selected);
%put &=selected;
More sophisticated pickers can be built using SAS/AF. Other possibilities include Stored Process prompt dialogs, SAS Studio snippets, or a SAS server page.

Related

COPY TO excel Sheet in foxpro

Is their any command in Foxpro that convert the DBF into a particular excel sheet.
I have three DBF(dbf_1, dbf_2, dbf_3). My current program convert the file using copy to "filename.xls" type fox2x and then I will manually copy the consolidate all the sheet into one excel. For me, this method I using is alright but what if their are 20 or more dbf that I will consolidate. Is their any command in foxpro that convert the dbf's into one excel file. I already use the foxpro Automation but it is to slow.
No there isn't.
Also "copy to ... type fox2x". although better than many other type selections (such as csv and xls) should not be chosen when there are better ways.
You are saying automation is slow, but don't know if you are really finding automation slow, or if you have tried it in the ways that you shouldn't use to transfer data to Excel. The sample below, use one of the variations of my "vfp2excel" function and automation. It transfers sample Customer, Employee, Orders, OrdItems and Products data in 2.5 seconds on my machine. If you really meant it as slow then no dice, otherwise here is the sample:
* These represent complex SQL as a sample
Select emp_id,First_Name,Last_Name,;
Title,Notes ;
from (_samples+'\data\employee') ;
into Cursor crsEmployee ;
readwrite
Replace All Notes With Chrtran(Notes,Chr(13)+Chr(10),Chr(10))
Select cust_id,company,contact,Title,country,postalcode ;
from (_samples+'\data\customer') ;
into Cursor crsCustomer ;
nofilter
Select * ;
from (_samples+'\data\orders') ;
into Cursor crsOrders ;
nofilter
Select * ;
from (_samples+'\data\orditems') ;
into Cursor crsOrderDetail ;
nofilter
Select * ;
from (_samples+'\data\products') ;
into Cursor crsProducts ;
nofilter
* Now we want to get these on 3 sheets
* Sheet1: Employees only
* Sheet2: Customers only
* Sheet3: Orders, ordItems, Products layed out horizontally
Local oExcel
oExcel = Createobject("Excel.Application")
With oExcel
.DisplayAlerts = .F.
.Workbooks.Add
.Visible = .T.
With .ActiveWorkBook
For ix = 1 To 3 && We want 3 Sheets
If .sheets.Count < m.ix
.sheets.Add(,.sheets(.sheets.Count)) && Add new sheets
Endif
Endfor
* Name the sheets
.WorkSheets(1).Name = "Employees"
.WorkSheets(2).Name = "Customers"
.WorkSheets(3).Name = "Order, OrderDetail, Products" && max sheetname is 31 chars
* Start sending data
* First one has headers specified
VFP2Excel('crsEmployee', .WorkSheets(1).Range("A1"), ;
"Id,First Name,Last Name,Employee Title,Comments about employee" ) && To sheet1, start at A1
VFP2Excel('crsCustomer', .WorkSheets(2).Range("A1") ) && To sheet2, start at A1
VFP2Excel('crsOrders', .WorkSheets(3).Range("A1") ) && To sheet3, start at A1
* Need to know where to put next
* Leave 2 columns empty - something like 'G1'
lcRange = _GetChar(.WorkSheets(3).UsedRange.Columns.Count + 3) + '1'
* To sheet3, start at next to previous
VFP2Excel('crsOrderDetail', .WorkSheets(3).Range(m.lcRange) )
lcRange = _GetChar(.WorkSheets(3).UsedRange.Columns.Count + 3) + '1'
* To sheet3, start at next to previous
VFP2Excel('crsProducts', .WorkSheets(3).Range(m.lcRange) )
#Define xlJustify -4130
#Define xlTop -4160
* I just happen to know notes in at column 5 from SQL
* No need to query from excel to keep code simple
* Lets format that column specially instead of leaving
* at the mercy of Excel's autofitting
.WorkSheets(1).UsedRange.VerticalAlignment = xlTop && set all to top
With .WorkSheets(1).Columns(5)
.ColumnWidth = 80 && 80 chars width
.WrapText = .T.
* .HorizontalAlignment = xlJustify && doesn't work good always
Endwith
* Finally some cosmetic stuff
For ix=1 To 3
With .WorkSheets(m.ix)
.Columns.AutoFit
.Rows.AutoFit
Endwith
Endfor
.WorkSheets(1).Activate
Endwith
Endwith
* Author: Cetin Basoz
* This is based on earlier VFP2Excel function codes
* that has been published on the internet, at various sites
* since 2001. Not to be messed with others' code who named the same but has
* nothing to do with the approaches taken here (unless copy & pasted and claimed
* to be their own work, < s > that happens).
Procedure VFP2Excel(tcCursorName, toRange, tcHeaders, tnPrefferredWidthForMemo)
* tcCursorName
* toRange
* tcHeaders: Optional. Defaults to field headers
* tnPrefferredWidthForMemo: Optional. Default 80
* Function VFP2Excel
tcCursorName = Evl(m.tcCursorName,Alias())
tnPrefferredWidthForMemo = Evl(m.tnPrefferredWidthForMemo,80)
Local loConn As AdoDB.Connection, loRS As AdoDB.Recordset,;
lcTemp,lcTempDb, oExcel,ix, lcFieldName, lcHeaders
lnSelect = Select()
lcTemp = Forcepath(Sys(2015)+'.dbf',Sys(2023))
lcTempDb = Forcepath(Sys(2015)+'.dbc',Sys(2023))
Create Database (m.lcTempDb)
Select * From (m.tcCursorName) Into Table (m.lcTemp) Database (m.lcTempDb)
Local Array aMemo[1]
Local nMemoCount
nMemoCount = 0
lcHeaders = ''
For ix = 1 To Fcount()
lcFieldName = Field(m.ix)
If Type(Field(m.ix))='M'
nMemoCount = m.nMemoCount + 1
Dimension aMemo[m.nMemoCount]
aMemo[m.nMemoCount] = m.ix
Replace All &lcFieldName With Chrtran(&lcFieldName,Chr(13)+Chr(10),Chr(10))
Endif
lcHeaders = m.lcHeaders + Iif(Empty(m.lcHeaders),'',',')+Proper(m.lcFieldName)
Endfor
tcHeaders = Evl(m.tcHeaders,m.lcHeaders)
Use In (Juststem(m.lcTemp))
Close Databases
Set Database To
loStream = Createobject('AdoDb.Stream')
loConn = Createobject('ADODB.Connection')
loRS = Createobject("ADODB.Recordset")
loConn.ConnectionString = "Provider=VFPOLEDB;Data Source="+m.lcTempDb
loConn.Open()
loRS = loConn.Execute("select * from "+m.lcTemp)
loRS.Save( loStream )
loRS.Close
loConn.Close
Erase (m.lcTemp)
* Use first row for headers
Local Array aHeader[1]
loRS.Open( loStream )
toRange.Offset(1,0).CopyFromRecordSet( loRS ) && Copy data starting from headerrow + 1
Set Safety Off
Delete Database (m.lcTempDb) Deletetables
Select (m.lnSelect)
For ix=1 To Iif( !Empty(m.tcHeaders), ;
ALINES(aHeader, m.tcHeaders,1,','), ;
loRS.Fields.Count )
toRange.Offset(0,m.ix-1).Value = ;
Iif( !Empty(m.tcHeaders), ;
aHeader[m.ix], ;
Proper(loRS.Fields(m.ix-1).Name) )
toRange.Offset(0,m.ix-1).Font.Bold = .T.
Endfor
#Define xlJustify -4130
#Define xlTop -4160
* This part is cosmetic
toRange.WorkSheet.Activate
With toRange.WorkSheet.UsedRange
.VerticalAlignment = xlTop && set all to top
For ix=1 To m.nMemoCount
With .Columns(aMemo[m.ix])
.ColumnWidth = m.tnPrefferredWidthForMemo && 80 chars width
.WrapText = .T.
Endwith
Endfor
.Columns.AutoFit
.Rows.AutoFit
Endwith
Endproc
* Return A, AA, BC etc noation for nth column
Function _GetChar
Lparameters tnColumn && Convert tnvalue to Excel alpha notation
If m.tnColumn = 0
Return ""
Endif
If m.tnColumn <= 26
Return Chr(Asc("A")-1+m.tnColumn)
Else
Return _GetChar(Int(Iif(m.tnColumn % 26 = 0,m.tnColumn - 1, m.tnColumn) / 26)) + ;
_GetChar((m.tnColumn-1)%26+1)
Endif
Endfunc
This is what I was looking for :-) I was trying with my knowledge of Excel Automation programming in Visual FoxPro but always got errors. My task was to create "n" Sheets from one big cursors which I want to parse regarding customer selection of attribute name from cursor to get also "n" Sheets. This sample is for 3 cursors and 3 Sheets and it is generic. But I need this for "n" cursors and one attribute which customer select to distinct and get "n" Sheets in one Excel file. So now I have dynamic procedure. I customized this code and solve my problem which I am trying to end for about 4 days. So again thank you for this code and off course I will not modify VFP2Excel procedure and wrote somewhere else my name. Thanks for help !
There is no native VFP function to do this, BUT, there is an awesome open source project which has a class that will make this very easy:
VFPx Workbook Xlsx - See it here on Github: XLSX Workbook for FoxPro
It has 3 magical functions that will do exactly what you asked for:
CreateWorkbook()
AddSheet()
SaveTableToWorkbook()
(Repeat commands 2 and 3 above for each DBF/Sheet you want to create)
It is well documented with a 54-page PDF and code sample that explains everything you'll need to know.

Reading a specific line in a .txt file using Visual Basic 6

I am currently working on an EDI file and now I want to load and read only a specific line on a .txt file and put that in a textbox. Here is what my data would look like
ISA*00* *00* *01*MKT71 *01*ADEV04 *160331*1001*U*00501*300000001*0*P*>~
GS*IN*MKT71*ADEV04*20160331*1001*300000001*X*005010~
ST*810*300000001~
BIG*20160316*BS13435**NONE~
REF * BM * DHL-2162693540~
REF*CR*5201~
Now I want to put only the data DHL-2162693540 located in line number 5 of the .txt file in my Textbox1.
The easiest and most straightforward way to do this is to read the first however many lines, and only keep the value of the last line read.
Here is some code that accomplishes that:
Private Sub ReadLines()
Dim MyLine As String
Dim DataLineNumber As Integer
DataLineNumber = 5
Open "C:\YourFileNameHere.txt" For Input As DataFile
Do While Not EOF(1) And LineNumber <= DataLineNumber
Line Input #DataFile, MyLine
DataLineNumber = DataLineNumber + 1
Loop
Close DataFile
MsgBox MyLine
End Sub

Convert numerical variable into SAS time variable

I imported a cvs file and have a variable 'departure time' in my sas file that consists of four digits, e.g. 0856 for 08.56 AM. I would like SAS to recognise it as time and want it to appear as 08:56. I have tried:
put DEP_TIME hhmm5.;
format DEP_TIME hhmm5.;
Doesn't work. Can't seem to figure this out.
Any clues?
Informat B8601TM.
33 data _null_;
34 t='0856';
35 time = input(t,B8601TM.);
36 format time time.;
37 put 'NOTE: ' (_all_)(=);
38 run;
NOTE: t=0856 time=8:56:00
I don't think there's an informat which will convert a 4-digit string to a time.
There's a couple of ways to do this, either using hms() and substr() functions, or a custom picture format :
proc format ;
picture TM
low-high = '00:00' ;
run ;
data want ;
set have ;
/* hms and substr method */
new_time1 = hms(substr(dep_time,1,2),substr(dep_time,3,2)) ;
format new_time1 hhmm5. ;
/* input / put with picture format */
new_time2 = input(put(input(dep_time,4.),tm.),time5.) ;
format new_time2 hhmm5. ;
run ;

How to convert visual foxpro 6 report to word

I have used following code to show a report.
select mem_no,com_name,owner,owner_cate,iif(empty(photo),"c:\edrs\memphoto\void.JPG",iif(file(photo),photo,"c:\edrs\memphoto\void.JPG")) as photo from own1 into curs own
REPO FORM c:\edrs\reports\rptsearch.frx TO PRINT PREVIEW NOCONS
Here rptsearch.frx contains some image. The following code export data to excel except image.
COPY TO "c:\documents and settings\administrator\desktop\a.xls" TYPE XLS
In case of image it shows only the path name. Now I need to know how I can convert this report in word so that I can have the images in the word report.
It looks like that you are creating a simple list with pictures. One of the easiest ways to do that is to use automation. ie:
Select mem_no,com_name,owner,owner_cate,;
iif(Empty(photo) Or !File(photo),"c:\edrs\memphoto\void.JPG",photo) As photo ;
from own1 ;
into Curs crsData ;
nofilter
#Define wdWord9TableBehavior 1
#Define wdAutoFitWindow 2
#Define wdStory 6
#Define wdCollapseEnd 0
#Define wdCellAlignVerticalCenter 1
#Define CR Chr(13)
Local Array laCaptions[5]
laCaptions[1] = 'Mem No'
laCaptions[2] = 'Com Name'
laCaptions[3] = 'Owner'
laCaptions[4] = 'Owner Cate'
laCaptions[5] = 'Photo'
Local nRows, nCols, ix
nRows = Reccount('crsData')+1
nCols = Fcount('crsData')+1
oWord = Createobject('Word.Application')
With m.oWord
oDocument = .Documents.Add
With m.oDocument.Tables.Add( m.oWord.Selection.Range, m.nRows, m.nCols)
.BorderS.InsideLineStyle = .F.
.BorderS.OutsideLineStyle = .F.
For ix=1 To Alen(laCaptions)
**** Add captions *****
.Cell(1,m.ix).Range.InsertAfter( laCaptions[m.ix] )
Endfor
Select crsData
Scan
For ix=1 To Fcount()-1 && last one is photo path
**** Add values to the different cells *****
.Cell(Recno()+1,m.ix).Range.InsertAfter( Eval(Field(m.ix)) )
Endfor
lcPhoto = crsData.photo
If File(m.lcPhoto) && Add photo if any
oDocument.InlineShapes.AddPicture( m.lcPhoto, .T., .T.,;
.Cell(Recno()+1,Fcount()).Range)
Endif
.Rows(Recno()+1).Cells.VerticalAlignment = wdCellAlignVerticalCenter
Endscan
Endwith
.Visible = .T.
Endwith
However, sending data to word this way would suffer from performance if you have many rows. You can use this for small data like an employee table or so. With larger data, instead of using automation, you could simply create an HTML document and word would open an HTML document.
There is no native way to do it. I would investigate FoxyPreviewer and use that to report to RTF, which Word can open.
Or do it the other way round with a mail merge in Word.
In addition to FoxyPreviewer, you could also use OLE Office automation to programmatically build the report. There are numerous examples online, and even a book, Microsoft Office Automation, written by Tamar E. Granor & Della Martin.
I have not done a lot with automation, only enough to get it basically verify it works, and discover it was very slow for what I was attempting to do.

How to save ole object (contains jpg,excel, pdf file)to file in powerbuilder

I have saved data in format of blob using powerbuilder ole control in oracle.
Now we want to convert these blob to files,
files are of different format(PDF, JPG,EXCEL,TEXT,DOC)
There are more than 1 Million files so it is not easy to do manually open and save using olecontrol.
Can we do it through script auto saving of blob to file in powerbuilder
Yes, it is possible:
Write a cursor in PowerBuilder embedded SQL to get for each record in your blob table the key and the and file extension (if you have those). The syntax for that kind of thing looks like this:
Long ll_Key
String ls_Ext
DECLARE GetBlobCursor CURSOR FOR
SELECT blob_key,
blob_extension
FROM blob_table ;
/* need to loop here while SQLCA.SQLCode is good */
FETCH GetBlobCursor
INTO :ll_Key,
:ls_Ext ;
Use a SELECTBLOB embedded SQL statement to get the blob data into a PowerBuilder BLOB variable:
Blob lblob_File
SELECTBLOB fileblob
INTO :lblob_File
FROM blobtable
WHERE blob_key = :ll_Key ;
Use FileOpen and FileWrite to write the blob with a valid file name and extension:
Long ll_Loops, ll_Step
Int li_File
String ls_Path
ls_Path = "<where do you want me?>." + String(ll_Key) + "." + ls_Ext
li_File = FileOpen(ls_Path, StreamMode!, Write!, LockWrite!, Append!)
If li_File > 0 Then
// Determine how many times to call FileWrite
ll_FileLen = Len(lblob_File)
If ll_FileLen > 32765 Then
If Mod(ll_FileLen, 32765) = 0 Then
ll_Loops = ll_FileLen/32765
Else
ll_Loops = (ll_FileLen/32765) + 1
End If
Else
ll_Loops = 1
End If
For ll_Step = 1 To ll_Loops
FileWrite(li_File,BlobMid(lblob_File,((ll_Step - 1)*32765) + 1, 32765))
Next
Else
//log the error, or handle
End If
FileClose(li_File)
Hope that gets you started.

Resources