1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-08 18:00:06 +00:00

Compare commits

...

33 Commits

Author SHA1 Message Date
Allan Bowe
d254870439 Merge pull request #164 from sasjs/mf_verify
fix: updating mm_x macros following fix to mf_verifymacvars
2022-02-06 20:58:55 +02:00
munja
303225cb85 fix: updating mm_x macros following fix to mf_verifymacvars 2022-02-06 19:57:52 +01:00
Allan Bowe
90de167643 Merge pull request #163 from sasjs/mf_verfiy
fix: updating mf_abort param in mf_verifymacvars, also fixing return …
2022-02-06 17:16:54 +02:00
munja
8ee997de8e fix: updating mf_abort param in mf_verifymacvars, also fixing return code as per documentation, adding a test, and updating the header info 2022-02-06 16:11:18 +01:00
Allan Bowe
e27f6ac716 Merge pull request #162 from sasjs/mf_getapploc
fix: adding support for testsetup and testteardown in mf_getapploc.sas
2022-02-05 22:56:10 +02:00
munja
ec4de95fcf fix: reset syscc for testterm syscc check 2022-02-05 21:55:50 +01:00
munja
df0fa95519 fix: adding sasjs/core dependency - see: https://github.com/sasjs/cli/issues/1113 2022-02-05 21:29:03 +01:00
munja
2fe7fba79b fix: adding support for testsetup and testteardown in mf_getapploc.sas 2022-02-05 21:19:26 +01:00
Allan Bowe
e40234ee29 Merge pull request #160 from sasjs/allanbowe-patch-1
chore(docs): Update README.md to clarify LUA prefixes
2022-02-04 20:56:51 +02:00
Allan Bowe
a287cc27a7 Update README.md 2022-02-04 18:56:19 +00:00
munja
921186eb74 fix: adding images to mp_streamfile.sas 2022-02-03 20:17:43 +01:00
munja
6fd215ceff fix: streaming output in mp_streamfile by default (eg when param not found) 2022-02-03 20:13:57 +01:00
Allan Bowe
0297509aa0 Merge pull request #159 from sasjs/woff3
fix: avoiding error and including test in mp_streamfile.sas
2022-02-03 20:59:11 +02:00
munja
c5a8bc745d chore: fix test 2022-02-03 19:58:12 +01:00
munja
36aa466561 fix: avoiding error and including a test 2022-02-03 19:44:21 +01:00
Allan Bowe
009485e5b9 Merge pull request #158 from sasjs/woff2
fix: support for WOFF2 and TTF
2022-02-03 19:51:39 +02:00
munja
eb01c8772d fix: support for WOFF2 and TTF 2022-02-03 18:51:07 +01:00
Allan Bowe
e3fb69928c Merge pull request #157 from sasjs/dependabot
chore: adding dependabot
2022-02-03 19:31:16 +02:00
munja
65afa14466 chore: removing leading spaces 2022-02-03 18:30:49 +01:00
munja
0176b19616 chore: adding dependabot 2022-02-03 18:29:31 +01:00
Allan Bowe
9f3dfd9a59 Merge pull request #156 from sasjs/csv
adding WOFF to mp_streamfile
2022-02-03 19:23:09 +02:00
munja
513ea354ab chore: updating headers in mp_streamfile and running all.sas 2022-02-03 16:45:42 +01:00
munja
7b686e11c9 feat: adding WOFF support to mp_streamfile (also re-ordering sections alphabetically) 2022-02-03 16:44:39 +01:00
munja
3997000266 fix: encoding issue in mp_ds2csv (option should have been in quotes) 2022-02-03 15:00:46 +01:00
Allan Bowe
6e177d4cae Merge pull request #155 from sasjs/cmplib
fix: auto-detect cmplib in mcfxx funcs, mp_ds2csv supports dates etc,…
2022-02-02 23:43:43 +02:00
munja
3554991ff8 fix: auto-detect cmplib in mcfxx funcs, mp_ds2csv supports dates etc, fix to mp_abort in viya due to abort cancel FILE hard stop (according to docs it should continue outside of the include) 2022-02-02 21:18:51 +01:00
Allan Bowe
58d2d6382a Merge pull request #154 from sasjs/errtext
fix: removing syserrortext as it breaks when commas are embedded
2022-02-02 21:36:30 +02:00
Allan Bowe
67f28a366c fix: removing syserrortext as it breaks when commas are embedded 2022-02-02 19:35:54 +00:00
Allan Bowe
64f53acce2 Merge pull request #153 from sasjs/mcf_fmtclass
Mcf fmtclass
2022-02-01 19:50:36 +02:00
munja
2e790f69a1 fix: removing view due to potential for vbufsize violations 2022-02-01 16:14:37 +01:00
munja
e62011d97e chore: updating variable name to fit doc header 2022-02-01 13:52:08 +01:00
munja
cd8d16d09f chore: updating all.sas 2022-02-01 13:48:48 +01:00
munja
9c61965d4b feat: new macro (mcf_getfmttype) to determine the type (DATE,DATETIME,TIME,CHAR,NUM) of a SAS format 2022-02-01 13:48:23 +01:00
32 changed files with 1190 additions and 327 deletions

9
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
schedule:
interval: monthly
open-pull-requests-limit: 3
allow:
- dependency-type: "production"

View File

@@ -81,7 +81,7 @@ Macros used for interfacing with SAS Viya.
Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper. Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper.
To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert your LUA into a data step with put statements, and create the macro wrapper with a `ml_` prefix. You can then use your module in any program by running: To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert all files with a ".lua" extension into a macro wrapper with an `ml_` prefix (embedding the necessary data step put statements). You can then use your module in any program by running:
```sas ```sas
/* compile the lua module */ /* compile the lua module */
@@ -95,8 +95,7 @@ endsubmit;
run; run;
``` ```
- X command enabled - Prefixes: _ml_
- Prefixes: _mmw_,_mmu_,_mmx_
## Installation ## Installation
@@ -129,11 +128,11 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
- _mf_ for macro functions (can be used in open code). - _mf_ for macro functions (can be used in open code).
- _ml_ for macros that are used to compile LUA modules - _ml_ for macros that are used to compile LUA modules
- _mm_ for metadata macros (interface with the metadata server). - _mm_ for metadata macros (interface with the metadata server).
- _mmx_ for macros that use metadata and are XCMD enabled - _mmx_ for macros that use metadata and are XCMD enabled (working on both windows and unix)
- _mp_ for macro procedures (which generate sas code) - _mp_ for macro procedures (which generate sas code)
- _ms_ for macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server) - _ms_ for macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server)
- _mv_ for macro procedures that will only work in Viya - _mv_ for macro procedures that will only work in Viya
- _mx_ for macros that are XCMD enabled - _mx_ for macros that are XCMD enabled (working on both windows and unix)
- follow verb-noun convention - follow verb-noun convention
- unix style line endings (lf) - unix style line endings (lf)
- individual lines should be no more than 80 characters long - individual lines should be no more than 80 characters long
@@ -196,6 +195,8 @@ When contributing to this library, it is therefore important to ensure that all
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major (breaking) release becomes necessary: We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major (breaking) release becomes necessary:
* mp_testservice.sas to be renamed as mp_execute.sas (as it doesn't actually test anything) * mp_testservice.sas to be renamed as mp_execute.sas (as it doesn't actually test anything)
* `insert_cmplib` option of mcf_xxx macros will be deprecated (the option is now checked automatically with value inserted only if needed)
* mcf_xxx macros to have `wrap=` option defaulted to YES for convenience
## Star Gazing ## Star Gazing

433
all.sas
View File

@@ -352,6 +352,7 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
@li /data @li /data
@li /jobs @li /jobs
@li /services @li /services
@li /tests
@li /tests/jobs @li /tests/jobs
@li /tests/services @li /tests/services
@li /tests/macros @li /tests/macros
@@ -383,9 +384,13 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
/** /**
* First check we are not in the tests/macros folder (which has no subfolders) * First check we are not in the tests/macros folder (which has no subfolders)
* or specifically in the testsetup or testteardown services
*/ */
%if %index(&pgm,/tests/macros/) %then %do; %if %index(&pgm,/tests/macros/)
%let root=%substr(&pgm,1,%index(&pgm,/tests/macros)-1); or %index(&pgm,/tests/testsetup)
or %index(&pgm,/tests/testteardown)
%then %do;
%let root=%substr(&pgm,1,%index(&pgm,/tests)-1);
&root &root
%return; %return;
%end; %end;
@@ -1802,7 +1807,7 @@ Usage:
%mend mf_uid;/** %mend mf_uid;/**
@file @file
@brief Checks if a set of macro variables exist / contain values. @brief Checks if a set of macro variables exist AND contain values.
@details Writes ERROR to log if abortType is SOFT, else will call %mf_abort. @details Writes ERROR to log if abortType is SOFT, else will call %mf_abort.
Usage: Usage:
@@ -1816,10 +1821,11 @@ Usage:
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_abort.sas @li mf_abort.sas
@param verifyvars space separated list of macro variable names @param [in] verifyvars Space separated list of macro variable names
@param makeupcase= set to YES to convert all variable VALUES to @param [in] makeupcase= (NO) Set to YES to convert all variable VALUES to
uppercase. uppercase.
@param mAbort= Abort Type. Default is SOFT (writes err to log). @param [in] mAbort= (SOFT) Abort Type. When SOFT, simply writes an err
message to the log.
Set to any other value to call mf_abort (which can be configured to abort in Set to any other value to call mf_abort (which can be configured to abort in
various fashions according to context). various fashions according to context).
@@ -1860,9 +1866,15 @@ Usage:
%goto exit_success; %goto exit_success;
%exit_err: %exit_err:
%if &mAbort=SOFT %then %put %str(ERR)OR: &abortmsg; %put %str(ERR)OR: &abortmsg;
%else %mf_abort(mac=mf_verifymacvars,type=&mabort,msg=&abortmsg); %mf_abort(iftrue=(&mabort ne SOFT),
mac=mf_verifymacvars,
msg=%str(&abortmsg)
)
0
%return;
%exit_success: %exit_success:
1
%mend mf_verifymacvars; %mend mf_verifymacvars;
/** /**
@@ -2106,7 +2118,10 @@ Usage:
%if %length(&mac)>0 %then %put NOTE- called by &mac; %if %length(&mac)>0 %then %put NOTE- called by &mac;
%put NOTE - &msg; %put NOTE - &msg;
%if %symexist(_SYSINCLUDEFILEDEVICE) %then %do; %if %symexist(_SYSINCLUDEFILEDEVICE)
/* abort cancel FILE does not restart outside the INCLUDE on Viya 3.5 */
and "&SYSPROCESSNAME " ne "Compute Server "
%then %do;
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do; %if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
data &errds; data &errds;
iftrue='1=1'; iftrue='1=1';
@@ -4666,10 +4681,28 @@ quit;
@brief Export a dataset to a CSV file WITH leading blanks @brief Export a dataset to a CSV file WITH leading blanks
@details Export a dataset to a file or fileref, retaining leading blanks. @details Export a dataset to a file or fileref, retaining leading blanks.
When using SASJS headerformat, the input statement is provided in the first
row of the CSV.
Usage: Usage:
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv") %mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
filename example temp;
%mp_ds2csv(sashelp.air,outref=example,headerformat=SASJS)
data; infile example; input;put _infile_; if _n_>5 then stop;run;
data _null_;
infile example;
input;
call symputx('stmnt',_infile_);
stop;
run;
data work.want;
infile example dsd firstobs=2;
input &stmnt;
run;
Why use mp_ds2csv over, say, proc export? Why use mp_ds2csv over, say, proc export?
1. Ability to retain leading blanks (this is a major one) 1. Ability to retain leading blanks (this is a major one)
@@ -4686,17 +4719,24 @@ quit;
@li LABEL - Use the variable label (or name, if blank) @li LABEL - Use the variable label (or name, if blank)
@li NAME - Use the variable name @li NAME - Use the variable name
@li SASJS - Used to create sasjs-formatted input CSVs, eg for use in @li SASJS - Used to create sasjs-formatted input CSVs, eg for use in
mp_testservice.sas mp_testservice.sas. This format will supply an input statement in the
first row, making ingestion by datastep a breeze. Special misisng values
will be prefixed with a period (eg `.A`) to enable ingestion on both SAS 9
and Viya. Dates / Datetimes etc are identified by the format type (lookup
with mcf_getfmttype.sas) and converted to human readable formats (not
numbers).
@param [out] outfile= The output filename - should be quoted. @param [out] outfile= The output filename - should be quoted.
@param [out] outref= (0) The output fileref (takes precedence if provided) @param [out] outref= (0) The output fileref (takes precedence if provided)
@param [in] outencoding= (0) The output encoding to use (unquoted) @param [in] outencoding= (0) The (quoted) output encoding to use, eg `"UTF-8"`
@param [in] termstr= (CRLF) The line seperator to use. For SASJS, will @param [in] termstr= (CRLF) The line seperator to use. For SASJS, will
always be CRLF. Valid values: always be CRLF. Valid values:
@li CRLF @li CRLF
@li LF @li LF
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mcf_getfmttype.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvarformat.sas
@li mf_getvarlist.sas @li mf_getvarlist.sas
@li mf_getvartype.sas @li mf_getvartype.sas
@@ -4713,7 +4753,7 @@ quit;
,termstr=CRLF ,termstr=CRLF
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local outloc delim i varlist var vcnt vat dsv vcom vmiss; %local outloc delim i varlist var vcnt vat dsv vcom vmiss fmttype vfmt;
%if not %sysfunc(exist(&ds)) %then %do; %if not %sysfunc(exist(&ds)) %then %do;
%put %str(WARN)ING: &ds does not exist; %put %str(WARN)ING: &ds does not exist;
@@ -4723,7 +4763,7 @@ quit;
%if %index(&ds,.)=0 %then %let ds=WORK.&ds; %if %index(&ds,.)=0 %then %let ds=WORK.&ds;
%if &outencoding=0 %then %let outencoding=; %if &outencoding=0 %then %let outencoding=;
%else %let outencoding=encoding="&outencoding"; %else %let outencoding=encoding=&outencoding;
%if &outref=0 %then %let outloc=&outfile; %if &outref=0 %then %let outloc=&outfile;
%else %let outloc=&outref; %else %let outloc=&outref;
@@ -4731,6 +4771,7 @@ quit;
%if &headerformat=SASJS %then %do; %if &headerformat=SASJS %then %do;
%let delim=","; %let delim=",";
%let termstr=CRLF; %let termstr=CRLF;
%mcf_getfmttype(wrap=YES)
%end; %end;
%else %if &dlm=COMMA %then %let delim=","; %else %if &dlm=COMMA %then %let delim=",";
%else %let delim=";"; %else %let delim=";";
@@ -4740,7 +4781,8 @@ quit;
/* first get headers */ /* first get headers */
data _null_; data _null_;
file &outloc &outencoding lrecl=32767 termstr=&termstr; file &outloc &outencoding lrecl=32767 termstr=&termstr;
length header $ 2000 varnm $32 dlm $1; length header $ 2000 varnm vfmt $32 dlm $1 fmttype $8;
call missing(of _all_);
dsid=open("&ds.","i"); dsid=open("&ds.","i");
num=attrn(dsid,"nvars"); num=attrn(dsid,"nvars");
dlm=&delim; dlm=&delim;
@@ -4755,7 +4797,14 @@ data _null_;
%end; %end;
%else %if &headerformat=SASJS %then %do; %else %if &headerformat=SASJS %then %do;
if vartype(dsid,i)='C' then header=cats(varnm,':$char',varlen(dsid,i),'.'); if vartype(dsid,i)='C' then header=cats(varnm,':$char',varlen(dsid,i),'.');
else do;
vfmt=coalescec(varfmt(dsid,i),'0');
fmttype=mcf_getfmttype(vfmt);
if fmttype='DATE' then header=cats(varnm,':date9.');
else if fmttype='DATETIME' then header=cats(varnm,':E8601DT26.6');
else if fmttype='TIME' then header=cats(varnm,':TIME12.');
else header=cats(varnm,':best.'); else header=cats(varnm,':best.');
end;
%end; %end;
%else %do; %else %do;
%put &sysmacroname: Invalid headerformat value (&headerformat); %put &sysmacroname: Invalid headerformat value (&headerformat);
@@ -4810,14 +4859,29 @@ data _null_;
%let vcom=; %let vcom=;
%end; %end;
%if %mf_getvartype(&ds,&var)=N %then %do; %if %mf_getvartype(&ds,&var)=N %then %do;
%if &headerformat = SASJS %then %do;
%let vcom=&delim;
%let fmttype=%sysfunc(mcf_getfmttype(%mf_getvarformat(&ds,&var)0));
%if &fmttype=DATE %then %let vfmt=DATE9.;
%else %if &fmttype=DATETIME %then %let vfmt=E8601DT26.6;
%else %if &fmttype=TIME %then %let vfmt=TIME12.;
%else %do;
%let vfmt=;
%let vcom=;
%end;
%end;
%else %let vcom=;
/* must use period - in order to work in both 9.4 and Viya 3.5 */ /* must use period - in order to work in both 9.4 and Viya 3.5 */
if missing(&var) and &var ne %sysfunc(getoption(MISSING)) then do; if missing(&var) and &var ne %sysfunc(getoption(MISSING)) then do;
&vmiss=cats('.',&var); &vmiss=cats('.',&var);
put &vmiss &vat; put &vmiss &vat;
end; end;
else put &var &vat; else put &var &vfmt &vcom &vat;
%end; %end;
%else %do; %else %do;
%if &i ne &vcnt %then %let vcom=&delim;
put &var &&vlen&i &vcom &vat; put &var &&vlen&i &vcom &vat;
%end; %end;
%end; %end;
@@ -5954,8 +6018,7 @@ filename &fref1 clear;
run; run;
%mp_abort( %mp_abort(
mac=&sysmacroname, mac=&sysmacroname,
msg=%str(Filter validation issues. ERR=%superq(SYSERRORTEXT) msg=%str(Filter validation issues.)
, WARN=%superq(SYSWARNINGTEXT) )
) )
%end; %end;
%let syscc=1008; %let syscc=1008;
@@ -7878,9 +7941,10 @@ https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/n1j5tcc0n2xczyn1
this dataset. this dataset.
It will then run an abort cancel FILE to stop the include running, and pass It will then run an abort cancel FILE to stop the include running, and pass
the dataset back. the dataset back.
NOTE - it is NOT possible to read this dataset as part of _this_ macro -
when running abort cancel FILE, ALL macros are closed, so instead it is IMPORTANT NOTE - it is NOT possible to read this dataset as part of _this_
necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of any macro wrappers. macro! When running abort cancel FILE, ALL macros are closed, so instead it
is necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of macro wrappers.
@version 9.4 @version 9.4
@@ -8137,7 +8201,7 @@ options
%put &sysmacroname: Switching to DATASTEP engine; %put &sysmacroname: Switching to DATASTEP engine;
%goto datastep; %goto datastep;
%end; %end;
data &tempds /view=&tempds;set &ds; data &tempds;set &ds;
%if &fmt=N %then format _numeric_ best32.;; %if &fmt=N %then format _numeric_ best32.;;
/* PRETTY is necessary to avoid line truncation in large files */ /* PRETTY is necessary to avoid line truncation in large files */
proc json out=&jref pretty proc json out=&jref pretty
@@ -8188,7 +8252,7 @@ options
%end; %end;
other = [best.]; other = [best.];
data &tempds/view=&tempds; data &tempds;
attrib _all_ label=''; attrib _all_ label='';
%do i=1 %to &numcols; %do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do; %if &&typelong&i=char or &fmt=Y %then %do;
@@ -8250,8 +8314,7 @@ options
%end; %end;
proc sql; proc sql;
drop view &tempds; drop table &colinfo, &tempds;
drop table &colinfo;
%if &showmeta=YES %then %do; %if &showmeta=YES %then %do;
data _null_; file &jref encoding='utf-8' mod; data _null_; file &jref encoding='utf-8' mod;
@@ -8517,7 +8580,7 @@ run;
/* do not proceed if no observations can be processed */ /* do not proceed if no observations can be processed */
%mp_abort(iftrue= (%sysfunc(getoption(OBS))=0) %mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
,mac=&sysmacroname ,mac=&sysmacroname
,msg=%str(options obs = 0. syserrortext=&syserrortext) ,msg=%str(cannot continue when options obs = 0)
) )
%if &ACTION=LOCK %then %do; %if &ACTION=LOCK %then %do;
@@ -10749,17 +10812,19 @@ create table &outds as
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt) %mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
@param [in] contenttype= (TEXTS) Either TEXT, ZIP, CSV, EXCEL
@param [in] inloc= /path/to/file.ext to be sent
@param [in] inref= fileref of file to be sent (if provided, overrides `inloc`)
@param [in] iftrue= (1=1) Provide a condition under which to execute.
@param [out] outname= the name of the file, as downloaded by the browser
@param [out] outref= (_webout) The destination where the file should be
streamed.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getplatform.sas @li mf_getplatform.sas
@li mp_binarycopy.sas @li mp_binarycopy.sas
@param contenttype= Either TEXT, ZIP, CSV, EXCEL (default TEXT)
@param inloc= /path/to/file.ext to be sent
@param inref= fileref of file to be sent (if provided, overrides `inloc`)
@param outname= the name of the file, as downloaded by the browser
@author Allan Bowe @author Allan Bowe
@source https://github.com/sasjs/core
**/ **/
@@ -10767,12 +10832,16 @@ create table &outds as
contenttype=TEXT contenttype=TEXT
,inloc= ,inloc=
,inref=0 ,inref=0
,iftrue=%str(1=1)
,outname= ,outname=
,outref=_webout
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%let contentype=%upcase(&contenttype); %if not(%eval(%unquote(&iftrue))) %then %return;
%local platform; %let platform=%mf_getplatform();
%let contentype=%upcase(&contenttype);
%let outref=%upcase(&outref);
%local platform; %let platform=%mf_getplatform();
/** /**
* check engine type to avoid the below err message: * check engine type to avoid the below err message:
@@ -10781,20 +10850,20 @@ create table &outds as
%local streamweb; %local streamweb;
%let streamweb=0; %let streamweb=0;
data _null_; data _null_;
set sashelp.vextfl(where=(upcase(fileref)="_WEBOUT")); set sashelp.vextfl(where=(upcase(fileref)="&outref"));
if xengine='STREAM' then call symputx('streamweb',1,'l'); if xengine='STREAM' then call symputx('streamweb',1,'l');
run; run;
%if &contentype=ZIP %then %do; %if &contentype=CSV %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do; %if &platform=SASMETA and &streamweb=1 %then %do;
data _null_; data _null_;
rc=stpsrv_header('Content-type','application/zip'); rc=stpsrv_header('Content-type','application/csv');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname"); rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run; run;
%end; %end;
%else %if &platform=SASVIYA %then %do; %else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip' filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/zip' contenttype='application/csv'
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%end; %end;
@@ -10807,11 +10876,52 @@ run;
run; run;
%end; %end;
%else %if &platform=SASVIYA %then %do; %else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls' filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
contenttype='application/vnd.ms-excel' contenttype='application/vnd.ms-excel'
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%end; %end;
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="image/%lowcase(&contenttype)";
%end;
%end;
%else %if &contentype=HTML %then %do;
%if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
contenttype="text/html";
%end;
%end;
%else %if &contentype=TEXT %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/text'
contentdisp="attachment; filename=&outname";
%end;
%end;
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="font/%lowcase(&contenttype)";
%end;
%end;
%else %if &contentype=XLSX %then %do; %else %if &contentype=XLSX %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do; %if &platform=SASMETA and &streamweb=1 %then %do;
data _null_; data _null_;
@@ -10821,54 +10931,34 @@ run;
run; run;
%end; %end;
%else %if &platform=SASVIYA %then %do; %else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls' filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
contenttype= contenttype=
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%end; %end;
%else %if &contentype=TEXT %then %do; %else %if &contentype=ZIP %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do; %if &platform=SASMETA and &streamweb=1 %then %do;
data _null_; data _null_;
rc=stpsrv_header('Content-type','application/text'); rc=stpsrv_header('Content-type','application/zip');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname"); rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run; run;
%end; %end;
%else %if &platform=SASVIYA %then %do; %else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt' filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
contenttype='application/text' contenttype='application/zip'
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%end; %end;
%else %if &contentype=CSV %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/csv');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/csv'
contentdisp="attachment; filename=&outname";
%end;
%end;
%else %if &contentype=HTML %then %do;
%if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
contenttype="text/html";
%end;
%end;
%else %do; %else %do;
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!; %put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;
%return;
%end; %end;
%if &inref ne 0 %then %do; %if &inref ne 0 %then %do;
%mp_binarycopy(inref=&inref,outref=_webout) %mp_binarycopy(inref=&inref,outref=&outref)
%end; %end;
%else %do; %else %do;
%mp_binarycopy(inloc="&inloc",outref=_webout) %mp_binarycopy(inloc="&inloc",outref=&outref)
%end; %end;
%mend mp_streamfile; %mend mp_streamfile;
@@ -12525,7 +12615,10 @@ run;
%&mD.put Executing &sysmacroname..sas; %&mD.put Executing &sysmacroname..sas;
%&mD.put _local_; %&mD.put _local_;
%mf_verifymacvars(tree name) %mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: tree name)
)
/** /**
* check tree exists * check tree exists
@@ -12749,7 +12842,10 @@ run;
%&mD.put Executing &sysmacroname..sas; %&mD.put Executing &sysmacroname..sas;
%&mD.put _local_; %&mD.put _local_;
%mf_verifymacvars(tree name) %mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: tree name)
)
/** /**
* check tree exists * check tree exists
@@ -13118,12 +13214,14 @@ run;
filename &frefin temp; filename &frefin temp;
filename &frefout temp; filename &frefout temp;
%mp_abort(iftrue= (
&engine=BASE & %mf_verifymacvars(libname libref engine servercontext tree)=0
)
,mac=&sysmacroname
,msg=%str(Empty inputs: libname libref engine servercontext tree)
)
%if &engine=BASE %then %do; %if &engine=BASE %then %do;
%mf_verifymacvars(libname libref engine servercontext tree)
/** /**
* Check that the ServerContext exists * Check that the ServerContext exists
*/ */
@@ -13414,7 +13512,12 @@ filename &frefout temp;
%&mD.put Executing mm_CreateSTP.sas; %&mD.put Executing mm_CreateSTP.sas;
%&mD.put _local_; %&mD.put _local_;
%mf_verifymacvars(stpname filename directory tree) %mp_abort(
iftrue=(%mf_verifymacvars(stpname filename directory tree)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: stpname filename directory tree)
)
%mp_dropmembers(%scan(&outds,2,.)) %mp_dropmembers(%scan(&outds,2,.))
/** /**
@@ -13861,7 +13964,7 @@ data _null_;
put ' %put &sysmacroname: Switching to DATASTEP engine; '; put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; '; put ' %goto datastep; ';
put ' %end; '; put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' proc json out=&jref pretty ';
@@ -13912,7 +14015,7 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; put ' ';
put ' data &tempds/view=&tempds; '; put ' data &tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
@@ -13974,8 +14077,7 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop view &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' drop table &colinfo; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' data _null_; file &jref encoding=''utf-8'' mod; ';
@@ -19004,7 +19106,7 @@ data _null_;
put ' %put &sysmacroname: Switching to DATASTEP engine; '; put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; '; put ' %goto datastep; ';
put ' %end; '; put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' proc json out=&jref pretty ';
@@ -19055,7 +19157,7 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; put ' ';
put ' data &tempds/view=&tempds; '; put ' data &tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
@@ -19117,8 +19219,7 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop view &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' drop table &colinfo; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' data _null_; file &jref encoding=''utf-8'' mod; ';
@@ -23556,6 +23657,123 @@ run;
%mend ml_json; %mend ml_json;
/** /**
@file
@brief Returns the type of the format
@details
Returns the type, eg DATE / DATETIME / TIME (based on hard-coded lookup)
else CHAR / NUM.
This macro may be extended in the future to support custom formats - this
would necessitate a call to `dosubl()` for running a proc format with cntlout.
The function itself takes the following (positional) parameters:
| PARAMETER | DESCRIPTION |
|---|---|
|fmtnm| Format name to be tested. Can be with or without the w.d extension.|
Usage:
%mcf_getfmttype(wrap=YES, insert_cmplib=YES)
data _null_;
fmt1=mcf_getfmttype('DATE9.');
fmt2=mcf_getfmttype('DATETIME');
put (fmt:)(=);
run;
%put fmt3=%sysfunc(mcf_getfmttype(TIME9.));
Returns:
> fmt1=DATE fmt2=DATETIME
> fmt3=TIME
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4>
@li mcf_init.sas
<h4> Related Programs </h4>
@li mcf_getfmttype.test.sas
@li mp_init.sas
@todo "Custom Format Lookups" To enable site-specific formats, make
use of a set of SASJS_FMTLIST_(DATATYPE) global variables.
**/
%macro mcf_getfmttype(wrap=NO
,insert_cmplib=DEPRECATED
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_getfmttype)=1 %then %return;
%if &wrap=YES %then %do;
proc fcmp outlib=&lib..&cat..&pkg;
%end;
function mcf_getfmttype(fmtnm $) $8;
if substr(fmtnm,1,1)='$' then return('CHAR');
else do;
/* extract NAME */
length fmt $32;
fmt=scan(fmtnm,1,'.');
do while (
substr(fmt,length(fmt),1) in ('1','2','3','4','5','6','7','8','9','0')
);
if length(fmt)=1 then fmt='W';
else fmt=substr(fmt,1,length(fmt)-1);
end;
/* apply lookups */
if cats(fmt) in ('DATETIME','B8601DN','B8601DN','B8601DT','B8601DT'
,'B8601DZ','B8601DZ','DATEAMPM','DTDATE','DTMONYY','DTWKDATX','DTYEAR'
,'DTYYQC','E8601DN','E8601DN','E8601DT','E8601DT','E8601DZ','E8601DZ')
then return('DATETIME');
else if fmt in ('DATE','YYMMDD','B8601DA','B8601DA','DAY','DDMMYY'
,'DDMMYYB','DDMMYYC','DDMMYYD','DDMMYYN','DDMMYYP','DDMMYYS','DDMMYYx'
,'DOWNAME','E8601DA','E8601DA','JULDAY','JULIAN','MMDDYY','MMDDYYB'
,'MMDDYYC','MMDDYYD','MMDDYYN','MMDDYYP','MMDDYYS','MMDDYYx','MMYY'
,'MMYYC','MMYYD','MMYYN','MMYYP','MMYYS','MMYYx','MONNAME','MONTH'
,'MONYY','PDJULG','PDJULI','QTR','QTRR','WEEKDATE','WEEKDATX','WEEKDAY'
,'WEEKU','WEEKV','WEEKW','WORDDATE','WORDDATX','YEAR','YYMM','YYMMC'
,'YYMMD','YYMMDDB','YYMMDDC','YYMMDDD','YYMMDDN','YYMMDDP','YYMMDDS'
,'YYMMDDx','YYMMN','YYMMP','YYMMS','YYMMx','YYMON','YYQ','YYQC','YYQD'
,'YYQN','YYQP','YYQR','YYQRC','YYQRD','YYQRN','YYQRP','YYQRS','YYQRx'
,'YYQS','YYQx','YYQZ') then return('DATE');
else if fmt in ('TIME','B8601LZ','B8601LZ','B8601TM','B8601TM','B8601TZ'
,'B8601TZ','E8601LZ','E8601LZ','E8601TM','E8601TM','E8601TZ','E8601TZ'
,'HHMM','HOUR','MMSS','TIMEAMPM','TOD') then return('TIME');
else return('NUM');
end;
endsub;
%if &wrap=YES %then %do;
quit;
%end;
/* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;
%mend mcf_getfmttype;/**
@file @file
@brief Sets up the mcf_xx functions @brief Sets up the mcf_xx functions
@details @details
@@ -23633,12 +23851,12 @@ run;
> outa=3 outb=4 outc=5 outd=0 > outa=3 outb=4 outc=5 outd=0
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper. @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog. @param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package. @param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function. @param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mcf_init.sas @li mcf_init.sas
@@ -23650,12 +23868,12 @@ run;
**/ **/
%macro mcf_length(wrap=NO %macro mcf_length(wrap=NO
,insert_cmplib=NO ,insert_cmplib=DEPRECATED
,lib=WORK ,lib=WORK
,cat=SASJS ,cat=SASJS
,pkg=UTILS ,pkg=UTILS
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_length)=1 %then %return; %if %mcf_init(mcf_length)=1 %then %return;
%if &wrap=YES %then %do; %if &wrap=YES %then %do;
@@ -23677,7 +23895,14 @@ endsub;
quit; quit;
%end; %end;
%if &insert_cmplib=YES %then %do; /* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat)); options insert=(CMPLIB=(&lib..&cat));
%end; %end;
@@ -23730,12 +23955,12 @@ endsub;
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper. @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog. @param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package. @param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function. @param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mcf_init.sas @li mcf_init.sas
@@ -23747,12 +23972,12 @@ endsub;
**/ **/
%macro mcf_stpsrv_header(wrap=NO %macro mcf_stpsrv_header(wrap=NO
,insert_cmplib=NO ,insert_cmplib=DEPRECATED
,lib=WORK ,lib=WORK
,cat=SASJS ,cat=SASJS
,pkg=UTILS ,pkg=UTILS
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(stpsrv_header)=1 %then %return; %if %mcf_init(stpsrv_header)=1 %then %return;
%if &wrap=YES %then %do; %if &wrap=YES %then %do;
@@ -23779,7 +24004,14 @@ endsub;
quit; quit;
%end; %end;
%if &insert_cmplib=YES %then %do; /* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat)); options insert=(CMPLIB=(&lib..&cat));
%end; %end;
@@ -23817,12 +24049,12 @@ endsub;
run; run;
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper. @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog. @param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package. @param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function. @param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mcf_init.sas @li mcf_init.sas
@@ -23834,12 +24066,12 @@ endsub;
**/ **/
%macro mcf_string2file(wrap=NO %macro mcf_string2file(wrap=NO
,insert_cmplib=NO ,insert_cmplib=DEPRECATED
,lib=WORK ,lib=WORK
,cat=SASJS ,cat=SASJS
,pkg=UTILS ,pkg=UTILS
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_string2file)=1 %then %return; %if %mcf_init(mcf_string2file)=1 %then %return;
%if &wrap=YES %then %do; %if &wrap=YES %then %do;
@@ -23866,7 +24098,14 @@ endsub;
quit; quit;
%end; %end;
%if &insert_cmplib=YES %then %do; /* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat)); options insert=(CMPLIB=(&lib..&cat));
%end; %end;

View File

@@ -15,6 +15,7 @@
@li /data @li /data
@li /jobs @li /jobs
@li /services @li /services
@li /tests
@li /tests/jobs @li /tests/jobs
@li /tests/services @li /tests/services
@li /tests/macros @li /tests/macros
@@ -46,9 +47,13 @@
/** /**
* First check we are not in the tests/macros folder (which has no subfolders) * First check we are not in the tests/macros folder (which has no subfolders)
* or specifically in the testsetup or testteardown services
*/ */
%if %index(&pgm,/tests/macros/) %then %do; %if %index(&pgm,/tests/macros/)
%let root=%substr(&pgm,1,%index(&pgm,/tests/macros)-1); or %index(&pgm,/tests/testsetup)
or %index(&pgm,/tests/testteardown)
%then %do;
%let root=%substr(&pgm,1,%index(&pgm,/tests)-1);
&root &root
%return; %return;
%end; %end;

View File

@@ -1,6 +1,6 @@
/** /**
@file @file
@brief Checks if a set of macro variables exist / contain values. @brief Checks if a set of macro variables exist AND contain values.
@details Writes ERROR to log if abortType is SOFT, else will call %mf_abort. @details Writes ERROR to log if abortType is SOFT, else will call %mf_abort.
Usage: Usage:
@@ -14,10 +14,11 @@
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_abort.sas @li mf_abort.sas
@param verifyvars space separated list of macro variable names @param [in] verifyvars Space separated list of macro variable names
@param makeupcase= set to YES to convert all variable VALUES to @param [in] makeupcase= (NO) Set to YES to convert all variable VALUES to
uppercase. uppercase.
@param mAbort= Abort Type. Default is SOFT (writes err to log). @param [in] mAbort= (SOFT) Abort Type. When SOFT, simply writes an err
message to the log.
Set to any other value to call mf_abort (which can be configured to abort in Set to any other value to call mf_abort (which can be configured to abort in
various fashions according to context). various fashions according to context).
@@ -58,8 +59,14 @@
%goto exit_success; %goto exit_success;
%exit_err: %exit_err:
%if &mAbort=SOFT %then %put %str(ERR)OR: &abortmsg; %put %str(ERR)OR: &abortmsg;
%else %mf_abort(mac=mf_verifymacvars,type=&mabort,msg=&abortmsg); %mf_abort(iftrue=(&mabort ne SOFT),
mac=mf_verifymacvars,
msg=%str(&abortmsg)
)
0
%return;
%exit_success: %exit_success:
1
%mend mf_verifymacvars; %mend mf_verifymacvars;

View File

@@ -66,7 +66,10 @@
%if %length(&mac)>0 %then %put NOTE- called by &mac; %if %length(&mac)>0 %then %put NOTE- called by &mac;
%put NOTE - &msg; %put NOTE - &msg;
%if %symexist(_SYSINCLUDEFILEDEVICE) %then %do; %if %symexist(_SYSINCLUDEFILEDEVICE)
/* abort cancel FILE does not restart outside the INCLUDE on Viya 3.5 */
and "&SYSPROCESSNAME " ne "Compute Server "
%then %do;
%if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do; %if "*&_SYSINCLUDEFILEDEVICE*" ne "**" %then %do;
data &errds; data &errds;
iftrue='1=1'; iftrue='1=1';

View File

@@ -3,10 +3,28 @@
@brief Export a dataset to a CSV file WITH leading blanks @brief Export a dataset to a CSV file WITH leading blanks
@details Export a dataset to a file or fileref, retaining leading blanks. @details Export a dataset to a file or fileref, retaining leading blanks.
When using SASJS headerformat, the input statement is provided in the first
row of the CSV.
Usage: Usage:
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv") %mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
filename example temp;
%mp_ds2csv(sashelp.air,outref=example,headerformat=SASJS)
data; infile example; input;put _infile_; if _n_>5 then stop;run;
data _null_;
infile example;
input;
call symputx('stmnt',_infile_);
stop;
run;
data work.want;
infile example dsd firstobs=2;
input &stmnt;
run;
Why use mp_ds2csv over, say, proc export? Why use mp_ds2csv over, say, proc export?
1. Ability to retain leading blanks (this is a major one) 1. Ability to retain leading blanks (this is a major one)
@@ -23,17 +41,24 @@
@li LABEL - Use the variable label (or name, if blank) @li LABEL - Use the variable label (or name, if blank)
@li NAME - Use the variable name @li NAME - Use the variable name
@li SASJS - Used to create sasjs-formatted input CSVs, eg for use in @li SASJS - Used to create sasjs-formatted input CSVs, eg for use in
mp_testservice.sas mp_testservice.sas. This format will supply an input statement in the
first row, making ingestion by datastep a breeze. Special misisng values
will be prefixed with a period (eg `.A`) to enable ingestion on both SAS 9
and Viya. Dates / Datetimes etc are identified by the format type (lookup
with mcf_getfmttype.sas) and converted to human readable formats (not
numbers).
@param [out] outfile= The output filename - should be quoted. @param [out] outfile= The output filename - should be quoted.
@param [out] outref= (0) The output fileref (takes precedence if provided) @param [out] outref= (0) The output fileref (takes precedence if provided)
@param [in] outencoding= (0) The output encoding to use (unquoted) @param [in] outencoding= (0) The (quoted) output encoding to use, eg `"UTF-8"`
@param [in] termstr= (CRLF) The line seperator to use. For SASJS, will @param [in] termstr= (CRLF) The line seperator to use. For SASJS, will
always be CRLF. Valid values: always be CRLF. Valid values:
@li CRLF @li CRLF
@li LF @li LF
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mcf_getfmttype.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvarformat.sas
@li mf_getvarlist.sas @li mf_getvarlist.sas
@li mf_getvartype.sas @li mf_getvartype.sas
@@ -50,7 +75,7 @@
,termstr=CRLF ,termstr=CRLF
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local outloc delim i varlist var vcnt vat dsv vcom vmiss; %local outloc delim i varlist var vcnt vat dsv vcom vmiss fmttype vfmt;
%if not %sysfunc(exist(&ds)) %then %do; %if not %sysfunc(exist(&ds)) %then %do;
%put %str(WARN)ING: &ds does not exist; %put %str(WARN)ING: &ds does not exist;
@@ -60,7 +85,7 @@
%if %index(&ds,.)=0 %then %let ds=WORK.&ds; %if %index(&ds,.)=0 %then %let ds=WORK.&ds;
%if &outencoding=0 %then %let outencoding=; %if &outencoding=0 %then %let outencoding=;
%else %let outencoding=encoding="&outencoding"; %else %let outencoding=encoding=&outencoding;
%if &outref=0 %then %let outloc=&outfile; %if &outref=0 %then %let outloc=&outfile;
%else %let outloc=&outref; %else %let outloc=&outref;
@@ -68,6 +93,7 @@
%if &headerformat=SASJS %then %do; %if &headerformat=SASJS %then %do;
%let delim=","; %let delim=",";
%let termstr=CRLF; %let termstr=CRLF;
%mcf_getfmttype(wrap=YES)
%end; %end;
%else %if &dlm=COMMA %then %let delim=","; %else %if &dlm=COMMA %then %let delim=",";
%else %let delim=";"; %else %let delim=";";
@@ -77,7 +103,8 @@
/* first get headers */ /* first get headers */
data _null_; data _null_;
file &outloc &outencoding lrecl=32767 termstr=&termstr; file &outloc &outencoding lrecl=32767 termstr=&termstr;
length header $ 2000 varnm $32 dlm $1; length header $ 2000 varnm vfmt $32 dlm $1 fmttype $8;
call missing(of _all_);
dsid=open("&ds.","i"); dsid=open("&ds.","i");
num=attrn(dsid,"nvars"); num=attrn(dsid,"nvars");
dlm=&delim; dlm=&delim;
@@ -92,7 +119,14 @@ data _null_;
%end; %end;
%else %if &headerformat=SASJS %then %do; %else %if &headerformat=SASJS %then %do;
if vartype(dsid,i)='C' then header=cats(varnm,':$char',varlen(dsid,i),'.'); if vartype(dsid,i)='C' then header=cats(varnm,':$char',varlen(dsid,i),'.');
else do;
vfmt=coalescec(varfmt(dsid,i),'0');
fmttype=mcf_getfmttype(vfmt);
if fmttype='DATE' then header=cats(varnm,':date9.');
else if fmttype='DATETIME' then header=cats(varnm,':E8601DT26.6');
else if fmttype='TIME' then header=cats(varnm,':TIME12.');
else header=cats(varnm,':best.'); else header=cats(varnm,':best.');
end;
%end; %end;
%else %do; %else %do;
%put &sysmacroname: Invalid headerformat value (&headerformat); %put &sysmacroname: Invalid headerformat value (&headerformat);
@@ -147,14 +181,29 @@ data _null_;
%let vcom=; %let vcom=;
%end; %end;
%if %mf_getvartype(&ds,&var)=N %then %do; %if %mf_getvartype(&ds,&var)=N %then %do;
%if &headerformat = SASJS %then %do;
%let vcom=&delim;
%let fmttype=%sysfunc(mcf_getfmttype(%mf_getvarformat(&ds,&var)0));
%if &fmttype=DATE %then %let vfmt=DATE9.;
%else %if &fmttype=DATETIME %then %let vfmt=E8601DT26.6;
%else %if &fmttype=TIME %then %let vfmt=TIME12.;
%else %do;
%let vfmt=;
%let vcom=;
%end;
%end;
%else %let vcom=;
/* must use period - in order to work in both 9.4 and Viya 3.5 */ /* must use period - in order to work in both 9.4 and Viya 3.5 */
if missing(&var) and &var ne %sysfunc(getoption(MISSING)) then do; if missing(&var) and &var ne %sysfunc(getoption(MISSING)) then do;
&vmiss=cats('.',&var); &vmiss=cats('.',&var);
put &vmiss &vat; put &vmiss &vat;
end; end;
else put &var &vat; else put &var &vfmt &vcom &vat;
%end; %end;
%else %do; %else %do;
%if &i ne &vcnt %then %let vcom=&delim;
put &var &&vlen&i &vcom &vat; put &var &&vlen&i &vcom &vat;
%end; %end;
%end; %end;

View File

@@ -96,8 +96,7 @@ filename &fref1 clear;
run; run;
%mp_abort( %mp_abort(
mac=&sysmacroname, mac=&sysmacroname,
msg=%str(Filter validation issues. ERR=%superq(SYSERRORTEXT) msg=%str(Filter validation issues.)
, WARN=%superq(SYSWARNINGTEXT) )
) )
%end; %end;
%let syscc=1008; %let syscc=1008;

View File

@@ -51,9 +51,10 @@ https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/n1j5tcc0n2xczyn1
this dataset. this dataset.
It will then run an abort cancel FILE to stop the include running, and pass It will then run an abort cancel FILE to stop the include running, and pass
the dataset back. the dataset back.
NOTE - it is NOT possible to read this dataset as part of _this_ macro -
when running abort cancel FILE, ALL macros are closed, so instead it is IMPORTANT NOTE - it is NOT possible to read this dataset as part of _this_
necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of any macro wrappers. macro! When running abort cancel FILE, ALL macros are closed, so instead it
is necessary to invoke "%mp_abort(mode=INCLUDE)" OUTSIDE of macro wrappers.
@version 9.4 @version 9.4

View File

@@ -131,7 +131,7 @@
%put &sysmacroname: Switching to DATASTEP engine; %put &sysmacroname: Switching to DATASTEP engine;
%goto datastep; %goto datastep;
%end; %end;
data &tempds /view=&tempds;set &ds; data &tempds;set &ds;
%if &fmt=N %then format _numeric_ best32.;; %if &fmt=N %then format _numeric_ best32.;;
/* PRETTY is necessary to avoid line truncation in large files */ /* PRETTY is necessary to avoid line truncation in large files */
proc json out=&jref pretty proc json out=&jref pretty
@@ -182,7 +182,7 @@
%end; %end;
other = [best.]; other = [best.];
data &tempds/view=&tempds; data &tempds;
attrib _all_ label=''; attrib _all_ label='';
%do i=1 %to &numcols; %do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do; %if &&typelong&i=char or &fmt=Y %then %do;
@@ -244,8 +244,7 @@
%end; %end;
proc sql; proc sql;
drop view &tempds; drop table &colinfo, &tempds;
drop table &colinfo;
%if &showmeta=YES %then %do; %if &showmeta=YES %then %do;
data _null_; file &jref encoding='utf-8' mod; data _null_; file &jref encoding='utf-8' mod;

View File

@@ -85,7 +85,7 @@ run;
/* do not proceed if no observations can be processed */ /* do not proceed if no observations can be processed */
%mp_abort(iftrue= (%sysfunc(getoption(OBS))=0) %mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
,mac=&sysmacroname ,mac=&sysmacroname
,msg=%str(options obs = 0. syserrortext=&syserrortext) ,msg=%str(cannot continue when options obs = 0)
) )
%if &ACTION=LOCK %then %do; %if &ACTION=LOCK %then %do;

View File

@@ -12,17 +12,19 @@
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt) %mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
@param [in] contenttype= (TEXTS) Either TEXT, ZIP, CSV, EXCEL
@param [in] inloc= /path/to/file.ext to be sent
@param [in] inref= fileref of file to be sent (if provided, overrides `inloc`)
@param [in] iftrue= (1=1) Provide a condition under which to execute.
@param [out] outname= the name of the file, as downloaded by the browser
@param [out] outref= (_webout) The destination where the file should be
streamed.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getplatform.sas @li mf_getplatform.sas
@li mp_binarycopy.sas @li mp_binarycopy.sas
@param contenttype= Either TEXT, ZIP, CSV, EXCEL (default TEXT)
@param inloc= /path/to/file.ext to be sent
@param inref= fileref of file to be sent (if provided, overrides `inloc`)
@param outname= the name of the file, as downloaded by the browser
@author Allan Bowe @author Allan Bowe
@source https://github.com/sasjs/core
**/ **/
@@ -30,12 +32,16 @@
contenttype=TEXT contenttype=TEXT
,inloc= ,inloc=
,inref=0 ,inref=0
,iftrue=%str(1=1)
,outname= ,outname=
,outref=_webout
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%let contentype=%upcase(&contenttype); %if not(%eval(%unquote(&iftrue))) %then %return;
%local platform; %let platform=%mf_getplatform();
%let contentype=%upcase(&contenttype);
%let outref=%upcase(&outref);
%local platform; %let platform=%mf_getplatform();
/** /**
* check engine type to avoid the below err message: * check engine type to avoid the below err message:
@@ -44,20 +50,20 @@
%local streamweb; %local streamweb;
%let streamweb=0; %let streamweb=0;
data _null_; data _null_;
set sashelp.vextfl(where=(upcase(fileref)="_WEBOUT")); set sashelp.vextfl(where=(upcase(fileref)="&outref"));
if xengine='STREAM' then call symputx('streamweb',1,'l'); if xengine='STREAM' then call symputx('streamweb',1,'l');
run; run;
%if &contentype=ZIP %then %do; %if &contentype=CSV %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do; %if &platform=SASMETA and &streamweb=1 %then %do;
data _null_; data _null_;
rc=stpsrv_header('Content-type','application/zip'); rc=stpsrv_header('Content-type','application/csv');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname"); rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run; run;
%end; %end;
%else %if &platform=SASVIYA %then %do; %else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip' filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/zip' contenttype='application/csv'
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%end; %end;
@@ -70,11 +76,52 @@ run;
run; run;
%end; %end;
%else %if &platform=SASVIYA %then %do; %else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls' filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
contenttype='application/vnd.ms-excel' contenttype='application/vnd.ms-excel'
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%end; %end;
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="image/%lowcase(&contenttype)";
%end;
%end;
%else %if &contentype=HTML %then %do;
%if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
contenttype="text/html";
%end;
%end;
%else %if &contentype=TEXT %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/text'
contentdisp="attachment; filename=&outname";
%end;
%end;
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="font/%lowcase(&contenttype)";
%end;
%end;
%else %if &contentype=XLSX %then %do; %else %if &contentype=XLSX %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do; %if &platform=SASMETA and &streamweb=1 %then %do;
data _null_; data _null_;
@@ -84,54 +131,34 @@ run;
run; run;
%end; %end;
%else %if &platform=SASVIYA %then %do; %else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls' filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
contenttype= contenttype=
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%end; %end;
%else %if &contentype=TEXT %then %do; %else %if &contentype=ZIP %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do; %if &platform=SASMETA and &streamweb=1 %then %do;
data _null_; data _null_;
rc=stpsrv_header('Content-type','application/text'); rc=stpsrv_header('Content-type','application/zip');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname"); rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run; run;
%end; %end;
%else %if &platform=SASVIYA %then %do; %else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt' filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
contenttype='application/text' contenttype='application/zip'
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%end; %end;
%else %if &contentype=CSV %then %do;
%if &platform=SASMETA and &streamweb=1 %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/csv');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
run;
%end;
%else %if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
contenttype='application/csv'
contentdisp="attachment; filename=&outname";
%end;
%end;
%else %if &contentype=HTML %then %do;
%if &platform=SASVIYA %then %do;
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
contenttype="text/html";
%end;
%end;
%else %do; %else %do;
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!; %put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;
%return;
%end; %end;
%if &inref ne 0 %then %do; %if &inref ne 0 %then %do;
%mp_binarycopy(inref=&inref,outref=_webout) %mp_binarycopy(inref=&inref,outref=&outref)
%end; %end;
%else %do; %else %do;
%mp_binarycopy(inloc="&inloc",outref=_webout) %mp_binarycopy(inloc="&inloc",outref=&outref)
%end; %end;
%mend mp_streamfile; %mend mp_streamfile;

118
fcmp/mcf_getfmttype.sas Normal file
View File

@@ -0,0 +1,118 @@
/**
@file
@brief Returns the type of the format
@details
Returns the type, eg DATE / DATETIME / TIME (based on hard-coded lookup)
else CHAR / NUM.
This macro may be extended in the future to support custom formats - this
would necessitate a call to `dosubl()` for running a proc format with cntlout.
The function itself takes the following (positional) parameters:
| PARAMETER | DESCRIPTION |
|---|---|
|fmtnm| Format name to be tested. Can be with or without the w.d extension.|
Usage:
%mcf_getfmttype(wrap=YES, insert_cmplib=YES)
data _null_;
fmt1=mcf_getfmttype('DATE9.');
fmt2=mcf_getfmttype('DATETIME');
put (fmt:)(=);
run;
%put fmt3=%sysfunc(mcf_getfmttype(TIME9.));
Returns:
> fmt1=DATE fmt2=DATETIME
> fmt3=TIME
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4>
@li mcf_init.sas
<h4> Related Programs </h4>
@li mcf_getfmttype.test.sas
@li mp_init.sas
@todo "Custom Format Lookups" To enable site-specific formats, make
use of a set of SASJS_FMTLIST_(DATATYPE) global variables.
**/
%macro mcf_getfmttype(wrap=NO
,insert_cmplib=DEPRECATED
,lib=WORK
,cat=SASJS
,pkg=UTILS
)/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_getfmttype)=1 %then %return;
%if &wrap=YES %then %do;
proc fcmp outlib=&lib..&cat..&pkg;
%end;
function mcf_getfmttype(fmtnm $) $8;
if substr(fmtnm,1,1)='$' then return('CHAR');
else do;
/* extract NAME */
length fmt $32;
fmt=scan(fmtnm,1,'.');
do while (
substr(fmt,length(fmt),1) in ('1','2','3','4','5','6','7','8','9','0')
);
if length(fmt)=1 then fmt='W';
else fmt=substr(fmt,1,length(fmt)-1);
end;
/* apply lookups */
if cats(fmt) in ('DATETIME','B8601DN','B8601DN','B8601DT','B8601DT'
,'B8601DZ','B8601DZ','DATEAMPM','DTDATE','DTMONYY','DTWKDATX','DTYEAR'
,'DTYYQC','E8601DN','E8601DN','E8601DT','E8601DT','E8601DZ','E8601DZ')
then return('DATETIME');
else if fmt in ('DATE','YYMMDD','B8601DA','B8601DA','DAY','DDMMYY'
,'DDMMYYB','DDMMYYC','DDMMYYD','DDMMYYN','DDMMYYP','DDMMYYS','DDMMYYx'
,'DOWNAME','E8601DA','E8601DA','JULDAY','JULIAN','MMDDYY','MMDDYYB'
,'MMDDYYC','MMDDYYD','MMDDYYN','MMDDYYP','MMDDYYS','MMDDYYx','MMYY'
,'MMYYC','MMYYD','MMYYN','MMYYP','MMYYS','MMYYx','MONNAME','MONTH'
,'MONYY','PDJULG','PDJULI','QTR','QTRR','WEEKDATE','WEEKDATX','WEEKDAY'
,'WEEKU','WEEKV','WEEKW','WORDDATE','WORDDATX','YEAR','YYMM','YYMMC'
,'YYMMD','YYMMDDB','YYMMDDC','YYMMDDD','YYMMDDN','YYMMDDP','YYMMDDS'
,'YYMMDDx','YYMMN','YYMMP','YYMMS','YYMMx','YYMON','YYQ','YYQC','YYQD'
,'YYQN','YYQP','YYQR','YYQRC','YYQRD','YYQRN','YYQRP','YYQRS','YYQRx'
,'YYQS','YYQx','YYQZ') then return('DATE');
else if fmt in ('TIME','B8601LZ','B8601LZ','B8601TM','B8601TM','B8601TZ'
,'B8601TZ','E8601LZ','E8601LZ','E8601TM','E8601TM','E8601TZ','E8601TZ'
,'HHMM','HOUR','MMSS','TIMEAMPM','TOD') then return('TIME');
else return('NUM');
end;
endsub;
%if &wrap=YES %then %do;
quit;
%end;
/* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat));
%end;
%mend mcf_getfmttype;

View File

@@ -32,12 +32,12 @@
> outa=3 outb=4 outc=5 outd=0 > outa=3 outb=4 outc=5 outd=0
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper. @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog. @param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package. @param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function. @param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mcf_init.sas @li mcf_init.sas
@@ -49,12 +49,12 @@
**/ **/
%macro mcf_length(wrap=NO %macro mcf_length(wrap=NO
,insert_cmplib=NO ,insert_cmplib=DEPRECATED
,lib=WORK ,lib=WORK
,cat=SASJS ,cat=SASJS
,pkg=UTILS ,pkg=UTILS
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_length)=1 %then %return; %if %mcf_init(mcf_length)=1 %then %return;
%if &wrap=YES %then %do; %if &wrap=YES %then %do;
@@ -76,7 +76,14 @@ endsub;
quit; quit;
%end; %end;
%if &insert_cmplib=YES %then %do; /* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat)); options insert=(CMPLIB=(&lib..&cat));
%end; %end;

View File

@@ -47,12 +47,12 @@
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper. @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog. @param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package. @param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function. @param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mcf_init.sas @li mcf_init.sas
@@ -64,12 +64,12 @@
**/ **/
%macro mcf_stpsrv_header(wrap=NO %macro mcf_stpsrv_header(wrap=NO
,insert_cmplib=NO ,insert_cmplib=DEPRECATED
,lib=WORK ,lib=WORK
,cat=SASJS ,cat=SASJS
,pkg=UTILS ,pkg=UTILS
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(stpsrv_header)=1 %then %return; %if %mcf_init(stpsrv_header)=1 %then %return;
%if &wrap=YES %then %do; %if &wrap=YES %then %do;
@@ -96,7 +96,14 @@ endsub;
quit; quit;
%end; %end;
%if &insert_cmplib=YES %then %do; /* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat)); options insert=(CMPLIB=(&lib..&cat));
%end; %end;

View File

@@ -32,12 +32,12 @@
run; run;
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper. @param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
CMPLIB reference.
@param [out] lib= (work) The output library in which to create the catalog. @param [out] lib= (work) The output library in which to create the catalog.
@param [out] cat= (sasjs) The output catalog in which to create the package. @param [out] cat= (sasjs) The output catalog in which to create the package.
@param [out] pkg= (utils) The output package in which to create the function. @param [out] pkg= (utils) The output package in which to create the function.
Uses a 3 part format: libref.catalog.package Uses a 3 part format: libref.catalog.package
@param [out] insert_cmplib= DEPRECATED - The CMPLIB option is checked and
values inserted only if needed.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mcf_init.sas @li mcf_init.sas
@@ -49,12 +49,12 @@
**/ **/
%macro mcf_string2file(wrap=NO %macro mcf_string2file(wrap=NO
,insert_cmplib=NO ,insert_cmplib=DEPRECATED
,lib=WORK ,lib=WORK
,cat=SASJS ,cat=SASJS
,pkg=UTILS ,pkg=UTILS
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local i var cmpval found;
%if %mcf_init(mcf_string2file)=1 %then %return; %if %mcf_init(mcf_string2file)=1 %then %return;
%if &wrap=YES %then %do; %if &wrap=YES %then %do;
@@ -81,7 +81,14 @@ endsub;
quit; quit;
%end; %end;
%if &insert_cmplib=YES %then %do; /* insert the CMPLIB if not already there */
%let cmpval=%sysfunc(getoption(cmplib));
%let found=0;
%do i=1 %to %sysfunc(countw(&cmpval,%str( %(%))));
%let var=%scan(&cmpval,&i,%str( %(%)));
%if &var=&lib..&cat %then %let found=1;
%end;
%if &found=0 %then %do;
options insert=(CMPLIB=(&lib..&cat)); options insert=(CMPLIB=(&lib..&cat));
%end; %end;

View File

@@ -59,7 +59,10 @@
%&mD.put Executing &sysmacroname..sas; %&mD.put Executing &sysmacroname..sas;
%&mD.put _local_; %&mD.put _local_;
%mf_verifymacvars(tree name) %mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: tree name)
)
/** /**
* check tree exists * check tree exists

View File

@@ -47,7 +47,10 @@
%&mD.put Executing &sysmacroname..sas; %&mD.put Executing &sysmacroname..sas;
%&mD.put _local_; %&mD.put _local_;
%mf_verifymacvars(tree name) %mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: tree name)
)
/** /**
* check tree exists * check tree exists

View File

@@ -133,12 +133,14 @@ run;
filename &frefin temp; filename &frefin temp;
filename &frefout temp; filename &frefout temp;
%mp_abort(iftrue= (
&engine=BASE & %mf_verifymacvars(libname libref engine servercontext tree)=0
)
,mac=&sysmacroname
,msg=%str(Empty inputs: libname libref engine servercontext tree)
)
%if &engine=BASE %then %do; %if &engine=BASE %then %do;
%mf_verifymacvars(libname libref engine servercontext tree)
/** /**
* Check that the ServerContext exists * Check that the ServerContext exists
*/ */

View File

@@ -108,7 +108,12 @@
%&mD.put Executing mm_CreateSTP.sas; %&mD.put Executing mm_CreateSTP.sas;
%&mD.put _local_; %&mD.put _local_;
%mf_verifymacvars(stpname filename directory tree) %mp_abort(
iftrue=(%mf_verifymacvars(stpname filename directory tree)=0)
,mac=&sysmacroname
,msg=%str(Empty inputs: stpname filename directory tree)
)
%mp_dropmembers(%scan(&outds,2,.)) %mp_dropmembers(%scan(&outds,2,.))
/** /**

View File

@@ -164,7 +164,7 @@ data _null_;
put ' %put &sysmacroname: Switching to DATASTEP engine; '; put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; '; put ' %goto datastep; ';
put ' %end; '; put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' proc json out=&jref pretty ';
@@ -215,7 +215,7 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; put ' ';
put ' data &tempds/view=&tempds; '; put ' data &tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
@@ -277,8 +277,7 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop view &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' drop table &colinfo; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' data _null_; file &jref encoding=''utf-8'' mod; ';

432
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,7 @@
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true" "prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
}, },
"devDependencies": { "devDependencies": {
"@sasjs/cli": "3.6.0" "@sasjs/cli": "3.6.0",
"@sasjs/core": "4.4.4"
} }
} }

View File

@@ -59,6 +59,7 @@
"tests/sas9only" "tests/sas9only"
], ],
"programFolders": [], "programFolders": [],
"binaryFolders": [],
"deployConfig": { "deployConfig": {
"deployServicePack": true, "deployServicePack": true,
"deployScripts": [] "deployScripts": []

View File

@@ -0,0 +1,39 @@
/**
@file
@brief Testing mcf_getfmttype.sas macro
<h4> SAS Macros </h4>
@li mcf_getfmttype.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
%mp_assertscope(SNAPSHOT)
%mcf_getfmttype(wrap=YES, insert_cmplib=YES)
%mp_assertscope(COMPARE,ignorelist=SASJS_FUNCTIONS)
%mp_assert(
iftrue=(%sysfunc(mcf_getfmttype(DATE9.))=DATE),
desc=Check DATE format
)
%mp_assert(
iftrue=(%sysfunc(mcf_getfmttype($6))=CHAR),
desc=Check CHAR format
)
%mp_assert(
iftrue=(%sysfunc(mcf_getfmttype(8.))=NUM),
desc=Check NUM format
)
%mp_assert(
iftrue=(%sysfunc(mcf_getfmttype(E8601DT))=DATETIME),
desc=Check DATETIME format
)
/* test 2 - compile again test for warnings */
%mcf_getfmttype(wrap=YES, insert_cmplib=YES)
%mp_assert(
iftrue=(&syscc=0),
desc=Check syscc=0 after re-initialisation
)

View File

@@ -47,3 +47,19 @@
desc=Checking tests/macros appLoc matches (which has no subfolder), desc=Checking tests/macros appLoc matches (which has no subfolder),
outds=work.test_results outds=work.test_results
) )
%mp_assert(
iftrue=(
"%mf_getapploc(/some/area/tests/testsetup)"="/some/area"
),
desc=Checking tests/testsetup operation,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_getapploc(/some/area/tests/testteardown)"="/some/area"
),
desc=Checking tests/teardown operation,
outds=work.test_results
)

View File

@@ -0,0 +1,22 @@
/**
@file
@brief Testing mf_verifymacvars macro
<h4> SAS Macros </h4>
@li mf_verifymacvars.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
%let var1=x;
%let var2=y;
%mp_assertscope(SNAPSHOT)
%mp_assert(
iftrue=(%mf_verifymacvars(var1 var2)=1),
desc=Checking macvars exist,
outds=work.test_results
)
%mp_assertscope(COMPARE)

View File

@@ -94,3 +94,73 @@ run;
desc=Checking data row Test 3, desc=Checking data row Test 3,
outds=work.test_results outds=work.test_results
) )
/* test 4 - sasjs with compare */
filename example temp;
%mp_ds2csv(sashelp.air,outref=example,headerformat=SASJS)
data _null_; infile example; input;put _infile_; if _n_>5 then stop;run;
data _null_;
infile example;
input;
call symputx('stmnt',_infile_);
stop;
run;
data work.want;
infile example dsd firstobs=2;
input &stmnt;
run;
%mp_assert(
iftrue=(&syscc =0),
desc=Checking syscc prior to compare of sashelp.air,
outds=work.test_results
)
proc compare base=want compare=sashelp.air;
run;
%mp_assert(
iftrue=(&sysinfo le 41),
desc=Checking compare of sashelp.air,
outds=work.test_results
)
/* test 5 - sasjs with time/datetime/date */
filename f2 temp;
data work.test5;
do x=1 to 5;
y=x;
z=x;
end;
format x date9. y datetime19. z time.;
run;
%mp_ds2csv(work.test5,outref=f2,headerformat=SASJS)
data _null_; infile example; input;put _infile_; if _n_>5 then stop;run;
data _null_;
infile f2;
input;
putlog _infile_;
call symputx('stmnt2',_infile_);
stop;
run;
data work.want5;
infile f2 dsd firstobs=2;
input &stmnt2;
putlog _infile_;
run;
%mp_assert(
iftrue=(&syscc=0),
desc=Checking syscc prior to compare of test5,
outds=work.test_results
)
proc compare base=want5 compare=work.test5;
run;
%mp_assert(
iftrue=(&sysinfo le 41),
desc=Checking compare of work.test5,
outds=work.test_results
)

View File

@@ -0,0 +1,28 @@
/**
@file
@brief Testing mp_streamfile.sas macro
@details This is tricky to test as it streams to webout. For now just
check the compilation, and for macro leakage.
<h4> SAS Macros </h4>
@li mp_assert.sas
@li mp_assertscope.sas
@li mp_streamfile.sas
**/
%mp_assertscope(SNAPSHOT)
%mp_streamfile(iftrue=(1=0)
,contenttype=csv,inloc=/some/where.txt
,outname=myfile.txt
)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(&syscc=0),
desc=Checking error condition,
outds=work.test_results
)

View File

@@ -2,8 +2,18 @@
@file @file
@brief term file for tests @brief term file for tests
<h4> SAS Macros </h4>
@li mp_assert.sas
**/ **/
%mp_assert(
iftrue=(&syscc=0),
desc=Checking final error condition,
outds=work.test_results
)
%webout(OPEN) %webout(OPEN)
%webout(OBJ, TEST_RESULTS) %webout(OBJ, TEST_RESULTS)
%webout(CLOSE) %webout(CLOSE)

View File

@@ -76,3 +76,6 @@ run;
iftrue=(&syscc ne 0), iftrue=(&syscc ne 0),
desc=Check that non zero return code is returned if called job fails desc=Check that non zero return code is returned if called job fails
) )
/* set syscc to zero for final check in testterm */
%let syscc=0;

View File

@@ -308,7 +308,7 @@ data _null_;
put ' %put &sysmacroname: Switching to DATASTEP engine; '; put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; '; put ' %goto datastep; ';
put ' %end; '; put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; '; put ' data &tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty '; put ' proc json out=&jref pretty ';
@@ -359,7 +359,7 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; put ' ';
put ' data &tempds/view=&tempds; '; put ' data &tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
@@ -421,8 +421,7 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc sql; '; put ' proc sql; ';
put ' drop view &tempds; '; put ' drop table &colinfo, &tempds; ';
put ' drop table &colinfo; ';
put ' '; put ' ';
put ' %if &showmeta=YES %then %do; '; put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; '; put ' data _null_; file &jref encoding=''utf-8'' mod; ';