mirror of
https://github.com/sasjs/core.git
synced 2025-12-12 15:04:36 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b34322d94 | |||
| 8bb83deede | |||
| 79c81aa8a4 | |||
| bbbcf7d550 | |||
| 82184bc6be | |||
| efc731cfaa | |||
| da9a74ee14 | |||
| 94762d9381 | |||
| 03d9d805ff | |||
| 94416028b7 | |||
| 6cf5d4ef28 | |||
| e4ceaecfb2 | |||
|
|
2eb246c543 | ||
| d9954ae777 | |||
| 364dc9f07f | |||
| d96125c3cf | |||
| 506695be56 | |||
|
|
45f858db15 | ||
|
|
b4d97a063a | ||
|
|
4df8f3b4c2 | ||
|
|
11aa484996 | ||
|
|
b9fd79bd5e | ||
|
|
1beb30d0ff | ||
|
|
e334ea9b85 | ||
|
|
c090c8d53b | ||
|
|
659339bd98 | ||
|
|
4c333ae7b3 | ||
| b3a8b4323e | |||
| 0592206f2d | |||
| bedc2a443a | |||
| 6f86ed62a2 | |||
| def0cc8476 | |||
| 3a9029557e | |||
| 9dc3bcd513 | |||
| 2bcf6346ac | |||
| 0eccc169f5 | |||
| 493639fe4a | |||
| 4987d2fbbc | |||
| 1a35b357d6 | |||
| a7792d93e4 | |||
| 541dc31ad0 | |||
| abccafab7b | |||
| f6cec012da | |||
| d51be73017 | |||
| cafffbb509 | |||
| a88efacfab | |||
| cc7cc55022 | |||
| 15687be5d6 | |||
| d9a82c0bdf | |||
| 6f06e5540d | |||
| 6b782a4fa2 | |||
| efe4709dde | |||
| 5cb41041d9 | |||
| f50cb03fd3 | |||
| ac46489f11 | |||
|
|
5e45701e74 | ||
|
|
8caaacd9f0 | ||
|
|
91983e0a91 | ||
|
|
7b72f0ac94 | ||
|
|
3eae34d8b7 | ||
|
|
58358c916d | ||
|
|
578ef26cd5 | ||
|
|
33189743cd | ||
|
|
459beff4fa | ||
|
|
1c873afe57 | ||
|
|
2b683509ac | ||
|
|
dcccd1491d | ||
|
|
8d9b84037c | ||
|
|
029c1a29ed | ||
|
|
c4dbd5971f | ||
|
|
40ac3bba9a | ||
|
|
21946e74f1 | ||
|
|
6443e2d2ef | ||
|
|
17f03b7507 | ||
|
|
c6a18a4168 | ||
|
|
dd18d1d5a8 | ||
|
|
fc8a39bbca | ||
|
|
8beec7dc19 | ||
|
|
e3f6cb7b45 | ||
|
|
041aff9bc0 | ||
|
|
1986732573 | ||
|
|
958f509894 | ||
|
|
1373957031 | ||
|
|
8466acc7a7 | ||
|
|
45e646565f | ||
|
|
9d9a72220f | ||
|
|
53865a3909 | ||
|
|
98a0d185ff |
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = false
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
.DS_Store
|
||||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2018 (Allan Bowe)
|
Copyright 2020 (Allan Bowe)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|||||||
43
README.md
43
README.md
@@ -40,6 +40,27 @@ Documentation: https://sasjs.github.io/core.github.io/files.html
|
|||||||
- X command enabled
|
- X command enabled
|
||||||
- Prefixes: _mmw_,_mmu_,_mmx_
|
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||||
|
|
||||||
|
**lua** library
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
/* compile the lua module */
|
||||||
|
%ml_yourmodule()
|
||||||
|
|
||||||
|
/* Execute. Do not use the restart keyword! */
|
||||||
|
proc lua;
|
||||||
|
submit;
|
||||||
|
print(yourStuff);
|
||||||
|
endsubmit;
|
||||||
|
run;
|
||||||
|
```
|
||||||
|
|
||||||
|
- X command enabled
|
||||||
|
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
First, download the repo to a location your SAS system can access. Then update your sasautos path to include the components you wish to have available,eg:
|
First, download the repo to a location your SAS system can access. Then update your sasautos path to include the components you wish to have available,eg:
|
||||||
@@ -72,6 +93,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
|||||||
- _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
|
||||||
- _mx_ for macros that are XCMD enabled
|
- _mx_ for macros that are XCMD enabled
|
||||||
|
- _ml_ for macros that are used to compile LUA modules
|
||||||
- _mv_ for macros that will only work in Viya
|
- _mv_ for macros that will only work in Viya
|
||||||
- follow verb-noun convention
|
- follow verb-noun convention
|
||||||
- unix style line endings (lf)
|
- unix style line endings (lf)
|
||||||
@@ -91,7 +113,25 @@ The **Macro Core** documentation is created using [doxygen](http://www.doxygen.n
|
|||||||
- version. The EARLIEST SAS version in which this macro is known to work.
|
- version. The EARLIEST SAS version in which this macro is known to work.
|
||||||
- author. Author name, contact details optional
|
- author. Author name, contact details optional
|
||||||
|
|
||||||
All macros must be commented in the doxygen format, to enable the [online documentation](https://sasjs.github.io/core.github.io/).
|
All macros must be commented in the doxygen format, to enable the [online documentation](https://core.sasjs.io).
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
SAS code can contain one of two types of dependency - SAS Macros, and SAS Programs. When compiling projects using the [SASjs CLI](https://cli.sasjs.io) the doxygen header is scanned for ` @li` items under the following headers:
|
||||||
|
|
||||||
|
```
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mm_assignlib.sas
|
||||||
|
|
||||||
|
<h4> SAS Programs </h4>
|
||||||
|
@li somefile.ddl SOMEFREF
|
||||||
|
@li someprogram.sas FREFTWO
|
||||||
|
```
|
||||||
|
|
||||||
|
The CLI can then extract all the dependencies and insert as precode (SAS Macros) or in a temp engine fileref (SAS Programs) when creating SAS Jobs and Services.
|
||||||
|
|
||||||
|
When contributing to this library, it is therefore important to ensure that all dependencies are listed in the header in this format.
|
||||||
|
|
||||||
|
|
||||||
## Coding Standards
|
## Coding Standards
|
||||||
|
|
||||||
@@ -102,6 +142,7 @@ All macros must be commented in the doxygen format, to enable the [online docume
|
|||||||
- Mandatory parameters should be positional, all optional parameters should be keyword (var=) style.
|
- Mandatory parameters should be positional, all optional parameters should be keyword (var=) style.
|
||||||
- All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect.
|
- All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect.
|
||||||
- Avoid naming collisions! All macro variables should be local scope. Use system generated work tables where possible - eg `data ; set sashelp.class; run; data &output; set &syslast; run;`
|
- Avoid naming collisions! All macro variables should be local scope. Use system generated work tables where possible - eg `data ; set sashelp.class; run; data &output; set &syslast; run;`
|
||||||
|
- If you have a long-running SQL query, the use of a `quit;` statement is recommended in order to benefit from the timing statistics.
|
||||||
|
|
||||||
# General Notes
|
# General Notes
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
@cond
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1)
|
%macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||||
@@ -139,3 +140,5 @@
|
|||||||
%abort cancel;
|
%abort cancel;
|
||||||
%end;
|
%end;
|
||||||
%mend;
|
%mend;
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
@file mf_existfeature.sas
|
@file
|
||||||
@brief Checks whether a feature exists
|
@brief Checks whether a feature exists
|
||||||
@details Check to see if a feature is supported in your environment.
|
@details Check to see if a feature is supported in your environment.
|
||||||
Run without arguments to see a list of detectable features.
|
Run without arguments to see a list of detectable features.
|
||||||
@@ -7,19 +7,20 @@
|
|||||||
actual feature detection, as that is tricky / impossible to do
|
actual feature detection, as that is tricky / impossible to do
|
||||||
without generating errors in most cases.
|
without generating errors in most cases.
|
||||||
|
|
||||||
%put %mf_existfeature(PROCLUA);
|
%put %mf_existfeature(PROCLUA);
|
||||||
|
|
||||||
@param feature the feature to detect. Leave blank to list all in log.
|
@param feature the feature to detect. Leave blank to list all in log.
|
||||||
|
|
||||||
@return output returns 1 or 0 (or -1 if not found)
|
@return output returns 1 or 0 (or -1 if not found)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
|
|
||||||
|
|
||||||
@version 8
|
@version 8
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
**/
|
||||||
|
/** @cond */
|
||||||
|
|
||||||
%macro mf_existfeature(feature
|
%macro mf_existfeature(feature
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
@@ -39,4 +40,6 @@
|
|||||||
-1
|
-1
|
||||||
%put &sysmacroname: &feature not found;
|
%put &sysmacroname: &feature not found;
|
||||||
%end;
|
%end;
|
||||||
%mend;
|
%mend;
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
27
base/mf_existfileref.sas
Normal file
27
base/mf_existfileref.sas
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Checks whether a fileref exists
|
||||||
|
@details You can probably do without this macro as it is just a one liner.
|
||||||
|
Mainly it is here as a convenient way to remember the syntax!
|
||||||
|
|
||||||
|
@param fref the fileref to detect
|
||||||
|
|
||||||
|
@return output Returns 1 if found and 0 if not found. Note - it is possible
|
||||||
|
that the fileref is found, but the file does not (yet) exist. If you need
|
||||||
|
to test for this, you may as well use the fileref function directly.
|
||||||
|
|
||||||
|
@version 8
|
||||||
|
@author [Allan Bowe](https://www.linkedin.com/in/allanbowe/)
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_existfileref(fref
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if %sysfunc(fileref(&fref))=0 %then %do;
|
||||||
|
1
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
0
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
**/
|
||||||
|
/** @cond */
|
||||||
|
|
||||||
%macro mf_existvar(libds /* 2 part dataset name */
|
%macro mf_existvar(libds /* 2 part dataset name */
|
||||||
, var /* variable name */
|
, var /* variable name */
|
||||||
@@ -29,4 +30,6 @@
|
|||||||
%let rc=%sysfunc(close(&dsid));
|
%let rc=%sysfunc(close(&dsid));
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%mend;
|
%mend;
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
%put %mf_existVarList(sashelp.class, age sex name dummyvar)
|
%put %mf_existVarList(sashelp.class, age sex name dummyvar)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_abort.sas
|
@li mf_abort.sas
|
||||||
|
|
||||||
@param libds 2 part dataset or view reference
|
@param libds 2 part dataset or view reference
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
@cond
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mf_existvarlist(libds, varlist
|
%macro mf_existvarlist(libds, varlist
|
||||||
@@ -53,4 +54,6 @@
|
|||||||
0
|
0
|
||||||
%put Vars not found: &found;
|
%put Vars not found: &found;
|
||||||
%end;
|
%end;
|
||||||
%mend;
|
%mend;
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
@brief Returns the engine type of a SAS library
|
@brief Returns the engine type of a SAS library
|
||||||
@details Usage:
|
@details Usage:
|
||||||
|
|
||||||
%put %mf_getEngine(SASHELP);
|
%put %mf_getengine(SASHELP);
|
||||||
|
|
||||||
returns:
|
returns:
|
||||||
> V9
|
> V9
|
||||||
@@ -21,9 +21,11 @@
|
|||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
|
||||||
|
|
||||||
%macro mf_getEngine(libref
|
**/
|
||||||
|
/** @cond */
|
||||||
|
|
||||||
|
%macro mf_getengine(libref
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%local dsid engnum rc engine;
|
%local dsid engnum rc engine;
|
||||||
|
|
||||||
@@ -42,3 +44,5 @@
|
|||||||
&engine
|
&engine
|
||||||
|
|
||||||
%mend;
|
%mend;
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
@@ -10,8 +10,9 @@
|
|||||||
|
|
||||||
@param switch the param for which to return a platform specific variable
|
@param switch the param for which to return a platform specific variable
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_mval.sas
|
@li mf_mval.sas
|
||||||
|
@li mf_trimstr.sas
|
||||||
|
|
||||||
@version 9.4 / 3.4
|
@version 9.4 / 3.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -57,6 +58,6 @@
|
|||||||
%else 0;
|
%else 0;
|
||||||
%end;
|
%end;
|
||||||
%else %if &switch=VIYARESTAPI %then %do;
|
%else %if &switch=VIYARESTAPI %then %do;
|
||||||
%sysfunc(getoption(servicesbaseurl))
|
%mf_trimstr(%sysfunc(getoption(servicesbaseurl)),/)
|
||||||
%end;
|
%end;
|
||||||
%mend;
|
%mend;
|
||||||
@@ -3,11 +3,18 @@
|
|||||||
@brief Adds custom quotes / delimiters to a delimited string
|
@brief Adds custom quotes / delimiters to a delimited string
|
||||||
@details Can be used in open code, eg as follows:
|
@details Can be used in open code, eg as follows:
|
||||||
|
|
||||||
%put %mf_getquotedstr(blah blah blah);
|
%put %mf_getquotedstr(blah blah blah);
|
||||||
|
|
||||||
which returns:
|
which returns:
|
||||||
> 'blah','blah','blah'
|
> 'blah','blah','blah'
|
||||||
|
|
||||||
|
Alternatively:
|
||||||
|
|
||||||
|
%put %mf_getquotedstr(these words are double quoted,quote=D)
|
||||||
|
|
||||||
|
for:
|
||||||
|
> "these","words","are","double","quoted"
|
||||||
|
|
||||||
@param in_str the unquoted, spaced delimited string to transform
|
@param in_str the unquoted, spaced delimited string to transform
|
||||||
@param dlm= the delimeter to be applied to the output (default comma)
|
@param dlm= the delimeter to be applied to the output (default comma)
|
||||||
@param indlm= the delimeter used for the input (default is space)
|
@param indlm= the delimeter used for the input (default is space)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
@cond
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mf_getschema(libref
|
%macro mf_getschema(libref
|
||||||
@@ -38,3 +39,5 @@
|
|||||||
&schema
|
&schema
|
||||||
|
|
||||||
%mend;
|
%mend;
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Assigns and returns an unused fileref
|
@brief Assigns and returns an unused fileref
|
||||||
@details Use as follows:
|
@details
|
||||||
|
Use as follows:
|
||||||
|
|
||||||
%let fileref1=%mf_getuniquefileref();
|
%let fileref1=%mf_getuniquefileref();
|
||||||
%let fileref2=%mf_getuniquefileref();
|
%let fileref2=%mf_getuniquefileref();
|
||||||
%put &fileref1 &fileref2;
|
%put &fileref1 &fileref2;
|
||||||
|
|
||||||
which returns:
|
which returns:
|
||||||
|
|
||||||
> mcref0 mcref1
|
> mcref0 mcref1
|
||||||
|
|
||||||
@prefix= first part of fileref. Remember that filerefs can only be 8
|
@param prefix= first part of fileref. Remember that filerefs can only be 8
|
||||||
characters, so a 7 letter prefix would mean that `maxtries` should be 10.
|
characters, so a 7 letter prefix would mean that `maxtries` should be 10.
|
||||||
@param maxtries= the last part of the libref. Provide an integer value.
|
@param maxtries= the last part of the libref. Provide an integer value.
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
> mclib3
|
> mclib3
|
||||||
|
|
||||||
@prefix= first part of libref. Remember that librefs can only be 8 characters,
|
@param prefix= first part of libref. Remember that librefs can only be 8 characters,
|
||||||
so a 7 letter prefix would mean that maxtries should be 10.
|
so a 7 letter prefix would mean that maxtries should be 10.
|
||||||
@param maxtries= the last part of the libref. Provide an integer value.
|
@param maxtries= the last part of the libref. Provide an integer value.
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
%put %mf_getvalue(sashelp.class,name,filter=%quote(age=15));
|
%put %mf_getvalue(sashelp.class,name,filter=%quote(age=15));
|
||||||
%put %mf_getvalue(sashelp.class,name);
|
%put %mf_getvalue(sashelp.class,name);
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getattrn.sas
|
@li mf_getattrn.sas
|
||||||
|
|
||||||
@param libds dataset to query
|
@param libds dataset to query
|
||||||
|
|||||||
33
base/mf_isdir.sas
Normal file
33
base/mf_isdir.sas
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Checks whether a path is a valid directory
|
||||||
|
@details
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%let isdir=%mf_isdir(/tmp);
|
||||||
|
|
||||||
|
With thanks and full credit to Andrea Defronzo - https://www.linkedin.com/in/andrea-defronzo-b1a47460/
|
||||||
|
|
||||||
|
@param path full path of the file/directory to be checked
|
||||||
|
|
||||||
|
@return output returns 1 if path is a directory, 0 if it is not
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_isdir(path
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local rc did is_directory fref_t;
|
||||||
|
|
||||||
|
%let is_directory = 0;
|
||||||
|
%let rc = %sysfunc(filename(fref_t, %superq(path)));
|
||||||
|
%let did = %sysfunc(dopen(&fref_t.));
|
||||||
|
%if &did. ^= 0 %then %do;
|
||||||
|
%let is_directory = 1;
|
||||||
|
%let rc = %sysfunc(dclose(&did.));
|
||||||
|
%end;
|
||||||
|
%let rc = %sysfunc(filename(fref_t));
|
||||||
|
|
||||||
|
&is_directory
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
%put Number of observations=%mf_nobs(sashelp.class);
|
%put Number of observations=%mf_nobs(sashelp.class);
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getattrn.sas
|
@li mf_getattrn.sas
|
||||||
|
|
||||||
@param libds library.dataset
|
@param libds library.dataset
|
||||||
|
|||||||
49
base/mf_trimstr.sas
Normal file
49
base/mf_trimstr.sas
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
@file mf_trimstr.sas
|
||||||
|
@brief Removes character(s) from the end, if they exist
|
||||||
|
@details If the designated characters exist at the end of the string, they
|
||||||
|
are removed
|
||||||
|
|
||||||
|
%put %mf_trimstr(/blah/,/); * /blah;
|
||||||
|
%put %mf_trimstr(/blah/,h); * /blah/;
|
||||||
|
%put %mf_trimstr(/blah/,h/);* /bla;
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
|
||||||
|
|
||||||
|
@param basestr The string to be modified
|
||||||
|
@param trimstr The string to be removed from the end of `basestr`, if it exists
|
||||||
|
|
||||||
|
@return output returns result with the value of `trimstr` removed from the end
|
||||||
|
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_trimstr(basestr,trimstr);
|
||||||
|
%local baselen trimlen trimval;
|
||||||
|
|
||||||
|
/* return if basestr is shorter than trimstr (or 0) */
|
||||||
|
%let baselen=%length(%superq(basestr));
|
||||||
|
%let trimlen=%length(%superq(trimstr));
|
||||||
|
%if &baselen < &trimlen or &baselen=0 %then %return;
|
||||||
|
|
||||||
|
/* obtain the characters from the end of basestr */
|
||||||
|
%let trimval=%qsubstr(%superq(basestr)
|
||||||
|
,%length(%superq(basestr))-&trimlen+1
|
||||||
|
,&trimlen);
|
||||||
|
|
||||||
|
/* compare and if matching, chop it off! */
|
||||||
|
%if %superq(basestr)=%superq(trimstr) %then %do;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
%else %if %superq(trimval)=%superq(trimstr) %then %do;
|
||||||
|
%qsubstr(%superq(basestr),1,%length(%superq(basestr))-&trimlen)
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
&basestr
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
Returns:
|
Returns:
|
||||||
> 1
|
> 1
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_abort.sas
|
@li mf_abort.sas
|
||||||
|
|
||||||
@param verifyvars space separated list of macro variable names
|
@param verifyvars space separated list of macro variable names
|
||||||
|
|||||||
@@ -6,12 +6,19 @@
|
|||||||
results back to the client in an STP Web App context, or completely stop
|
results back to the client in an STP Web App context, or completely stop
|
||||||
in the case of a batch run.
|
in the case of a batch run.
|
||||||
|
|
||||||
|
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process
|
||||||
|
environments. This macro takes a unique approach - we set the SAS syscc to 0,
|
||||||
|
run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro
|
||||||
|
but don't close it! This provides a graceful abort for SAS web services in all
|
||||||
|
web enabled environments.
|
||||||
|
|
||||||
@param mac= to contain the name of the calling macro
|
@param mac= to contain the name of the calling macro
|
||||||
@param msg= message to be returned
|
@param msg= message to be returned
|
||||||
@param iftrue= supply a condition under which the macro should be executed.
|
@param iftrue= supply a condition under which the macro should be executed.
|
||||||
|
|
||||||
@version 9.4M3
|
@version 9.4M3
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
@cond
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
|
||||||
@@ -24,7 +31,7 @@
|
|||||||
%put NOTE - &msg;
|
%put NOTE - &msg;
|
||||||
|
|
||||||
/* Stored Process Server web app context */
|
/* Stored Process Server web app context */
|
||||||
%if %symexist(_metaperson)
|
%if %symexist(_metaperson)
|
||||||
or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" )
|
or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" )
|
||||||
%then %do;
|
%then %do;
|
||||||
options obs=max replace nosyntaxcheck mprint;
|
options obs=max replace nosyntaxcheck mprint;
|
||||||
@@ -65,8 +72,15 @@
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
%if %symexist(SYS_JES_JOB_URI) %then %do;
|
||||||
/* refer web service output to file service in one hit */
|
/* setup webout */
|
||||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json";
|
OPTIONS NOBOMFILE;
|
||||||
|
%if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
|
||||||
|
filename _webout temp lrecl=999999 mod;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||||
|
name="_webout.json" lrecl=999999 mod;
|
||||||
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
/* send response in SASjs JSON format */
|
/* send response in SASjs JSON format */
|
||||||
@@ -138,3 +152,4 @@
|
|||||||
%end;
|
%end;
|
||||||
%mend;
|
%mend;
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
applying CRLF line endings and converting embedded cr and crlf to lf.
|
applying CRLF line endings and converting embedded cr and crlf to lf.
|
||||||
|
|
||||||
usage:
|
usage:
|
||||||
|
|
||||||
fileref mycsv "/path/your/csv";
|
fileref mycsv "/path/your/csv";
|
||||||
%mp_cleancsv(in=mycsv,out=/path/new.csv)
|
%mp_cleancsv(in=mycsv,out=/path/new.csv)
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
@cond
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
|
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
|
||||||
@@ -65,4 +67,5 @@
|
|||||||
else put inchar $char1.;
|
else put inchar $char1.;
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
%mend;
|
%mend;
|
||||||
|
/** @endcond */
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
@param outds= a table containing the create statements (create_statement column)
|
@param outds= a table containing the create statements (create_statement column)
|
||||||
@param execute= `YES|NO` - default is NO. To actually create, use YES.
|
@param execute= `YES|NO` - default is NO. To actually create, use YES.
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Usage:
|
|||||||
;;;;
|
;;;;
|
||||||
%mp_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
|
%mp_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mm_createwebservice.sas
|
@li mm_createwebservice.sas
|
||||||
@li mv_createwebservice.sas
|
@li mv_createwebservice.sas
|
||||||
|
|||||||
144
base/mp_csv2ds.sas
Normal file
144
base/mp_csv2ds.sas
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
/**
|
||||||
|
@file mp_csv2ds.sas
|
||||||
|
@brief Efficient import of arbitrary CSV using a dataset as template
|
||||||
|
@details Used to import relevant columns from a large CSV using
|
||||||
|
a dataset to provide the types and lengths. Assumes that a header
|
||||||
|
row is provided, and datarows start on line 2. Extra columns in
|
||||||
|
both the CSV and base dataset are ignored.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
filename mycsv temp;
|
||||||
|
data _null_;
|
||||||
|
file mycsv;
|
||||||
|
put 'name,age,nickname';
|
||||||
|
put 'John,48,Jonny';
|
||||||
|
put 'Jennifer,23,Jen';
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_csv2ds(inref=mycsv,outds=myds,baseds=sashelp.class)
|
||||||
|
|
||||||
|
|
||||||
|
@param inref= fileref to the CSV
|
||||||
|
@param outds= output ds (lib.ds format)
|
||||||
|
@param view= Set to YES or NO to determine whether the output should be
|
||||||
|
a view or not. Default is NO (not a view).
|
||||||
|
@param baseds= Template dataset on which to create the input statement.
|
||||||
|
Is used to determine types, lengths, and any informats.
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mf_existds.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_csv2ds(inref=0,outds=0,baseds=0,view=NO);
|
||||||
|
|
||||||
|
%mp_abort(iftrue=( &inref=0 )
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(the INREF variable must be provided)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=( %superq(outds)=0 )
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(the OUTDS variable must be provided)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=( &baseds=0 )
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(the BASEDS variable must be provided)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=( &baseds=0 )
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(the BASEDS variable must be provided)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=( %mf_existds(&baseds)=0 )
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(the BASEDS dataset (&baseds) needs to be assigned, and to exist)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* count rows */
|
||||||
|
%local hasheader; %let hasheader=0;
|
||||||
|
data _null_;
|
||||||
|
if _N_ > 1 then do;
|
||||||
|
call symputx('hasheader',1,'l');
|
||||||
|
stop;
|
||||||
|
end;
|
||||||
|
infile &inref;
|
||||||
|
input;
|
||||||
|
run;
|
||||||
|
%mp_abort(iftrue=( &hasheader=0 )
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(No header row in &inref)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* get the variables in the CSV */
|
||||||
|
data _data_;
|
||||||
|
infile &inref;
|
||||||
|
input;
|
||||||
|
length name $32;
|
||||||
|
do i=1 to countc(_infile_,',')+1;
|
||||||
|
name=upcase(scan(_infile_,i,','));
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
%local csv_vars;%let csv_vars=&syslast;
|
||||||
|
|
||||||
|
/* get the variables in the dataset */
|
||||||
|
proc contents noprint data=&baseds
|
||||||
|
out=_data_ (keep=name type length format: informat);
|
||||||
|
run;
|
||||||
|
%local base_vars; %let base_vars=&syslast;
|
||||||
|
|
||||||
|
proc sql undo_policy=none;
|
||||||
|
create table &csv_vars as
|
||||||
|
select a.*
|
||||||
|
,b.type
|
||||||
|
,b.length
|
||||||
|
,b.format
|
||||||
|
,b.formatd
|
||||||
|
,b.formatl
|
||||||
|
,b.informat
|
||||||
|
from &csv_vars a
|
||||||
|
left join &base_vars b
|
||||||
|
on a.name=upcase(b.name)
|
||||||
|
order by i;
|
||||||
|
|
||||||
|
/* prepare the input statement */
|
||||||
|
%local instat dropvars;
|
||||||
|
data _null_;
|
||||||
|
set &syslast end=last;
|
||||||
|
length in dropvars $32767;
|
||||||
|
retain in dropvars;
|
||||||
|
if missing(type) then do;
|
||||||
|
informat='$1.';
|
||||||
|
dropvars=catx(' ',dropvars,name);
|
||||||
|
end;
|
||||||
|
else if missing(informat) then do;
|
||||||
|
if type=1 then informat='best.';
|
||||||
|
else informat=cats('$',length,'.');
|
||||||
|
end;
|
||||||
|
else informat=cats(informat,'.');
|
||||||
|
in=catx(' ',in,name,':',informat);
|
||||||
|
if last then do;
|
||||||
|
call symputx('instat',in,'l');
|
||||||
|
call symputx('dropvars',dropvars,'l');
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* import the CSV */
|
||||||
|
data &outds
|
||||||
|
%if %upcase(&view)=YES %then %do;
|
||||||
|
/view=&outds
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
infile &inref dsd firstobs=2;
|
||||||
|
input &instat;
|
||||||
|
%if %length(&dropvars)>0 %then %do;
|
||||||
|
drop &dropvars;
|
||||||
|
%end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Returns all files and subdirectories within a specified parent
|
@brief Returns all files and subdirectories within a specified parent
|
||||||
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
|
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
|
||||||
|
|
||||||
If getattrs=YES then the doptname / foptname functions are used to scan all
|
If getattrs=YES then the doptname / foptname functions are used to scan all
|
||||||
properties - any characters that are not valid in a SAS name (v7) are simply
|
properties - any characters that are not valid in a SAS name (v7) are simply
|
||||||
stripped, and the table is transposed so theat each property is a column
|
stripped, and the table is transposed so theat each property is a column
|
||||||
and there is one file per row. An attempt is made to get all properties
|
and there is one file per row. An attempt is made to get all properties
|
||||||
whether a file or folder, but some files/folders cannot be accessed, and so
|
whether a file or folder, but some files/folders cannot be accessed, and so
|
||||||
not all properties can / will be populated.
|
not all properties can / will be populated.
|
||||||
|
|
||||||
Credit for the rename approach:
|
Credit for the rename approach:
|
||||||
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
|
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
|
||||||
|
|
||||||
|
|
||||||
usage:
|
usage:
|
||||||
@@ -20,21 +20,25 @@
|
|||||||
|
|
||||||
%mp_dirlist(outds=cwdfileprops, getattrs=YES)
|
%mp_dirlist(outds=cwdfileprops, getattrs=YES)
|
||||||
|
|
||||||
@warning In a Unix environment, the existence of a named pipe will cause this
|
%mp_dirlist(fref=MYFREF)
|
||||||
|
|
||||||
|
@warning In a Unix environment, the existence of a named pipe will cause this
|
||||||
macro to hang. Therefore this tool should be used with caution in a SAS 9 web
|
macro to hang. Therefore this tool should be used with caution in a SAS 9 web
|
||||||
application, as it can use up all available multibridge sessions if requests
|
application, as it can use up all available multibridge sessions if requests
|
||||||
are resubmitted.
|
are resubmitted.
|
||||||
If anyone finds a way to positively identify a named pipe using SAS (without
|
If anyone finds a way to positively identify a named pipe using SAS (without
|
||||||
X CMD) do please raise an issue!
|
X CMD) do please raise an issue!
|
||||||
|
|
||||||
|
|
||||||
@param path= for which to return contents
|
@param path= for which to return contents
|
||||||
|
@param fref= Provide a DISK engine fileref as an alternative to PATH
|
||||||
@param outds= the output dataset to create
|
@param outds= the output dataset to create
|
||||||
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
|
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
|
||||||
all attributes for each file / folder.
|
all attributes for each file / folder.
|
||||||
|
|
||||||
|
|
||||||
@returns outds contains the following variables:
|
@returns outds contains the following variables:
|
||||||
|
- directory (containing folder)
|
||||||
- file_or_folder (file / folder)
|
- file_or_folder (file / folder)
|
||||||
- filepath (path/to/file.name)
|
- filepath (path/to/file.name)
|
||||||
- filename (just the file name)
|
- filename (just the file name)
|
||||||
@@ -47,18 +51,26 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_dirlist(path=%sysfunc(pathname(work))
|
%macro mp_dirlist(path=%sysfunc(pathname(work))
|
||||||
|
, fref=0
|
||||||
, outds=work.mp_dirlist
|
, outds=work.mp_dirlist
|
||||||
, getattrs=NO
|
, getattrs=NO
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%let getattrs=%upcase(&getattrs)XX;
|
%let getattrs=%upcase(&getattrs)XX;
|
||||||
|
|
||||||
data &outds (compress=no keep=file_or_folder filepath filename ext msg);
|
data &outds (compress=no keep=file_or_folder filepath filename ext msg directory);
|
||||||
length filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
|
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200;
|
||||||
rc = filename(fref, "&path");
|
%if &fref=0 %then %do;
|
||||||
|
rc = filename(fref, "&path");
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
fref="&fref";
|
||||||
|
rc=0;
|
||||||
|
%end;
|
||||||
if rc = 0 then do;
|
if rc = 0 then do;
|
||||||
did = dopen(fref);
|
did = dopen(fref);
|
||||||
|
directory=dinfo(did,'Directory');
|
||||||
if did=0 then do;
|
if did=0 then do;
|
||||||
putlog "NOTE: This directory is empty - &path";
|
putlog "NOTE: This directory is empty - " directory;
|
||||||
msg=sysmsg();
|
msg=sysmsg();
|
||||||
put _all_;
|
put _all_;
|
||||||
stop;
|
stop;
|
||||||
@@ -73,7 +85,8 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
|
|||||||
dnum = dnum(did);
|
dnum = dnum(did);
|
||||||
do i = 1 to dnum;
|
do i = 1 to dnum;
|
||||||
filename = dread(did, i);
|
filename = dread(did, i);
|
||||||
rc = filename(fref2, "&path/"!!filename);
|
filepath=cats(directory,'/',filename);
|
||||||
|
rc = filename(fref2,filepath);
|
||||||
midd=dopen(fref2);
|
midd=dopen(fref2);
|
||||||
dmsg=sysmsg();
|
dmsg=sysmsg();
|
||||||
if did > 0 then file_or_folder='folder';
|
if did > 0 then file_or_folder='folder';
|
||||||
@@ -82,12 +95,12 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
|
|||||||
fmsg=sysmsg();
|
fmsg=sysmsg();
|
||||||
if midf > 0 then file_or_folder='file';
|
if midf > 0 then file_or_folder='file';
|
||||||
rc=fclose(midf);
|
rc=fclose(midf);
|
||||||
|
|
||||||
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
|
if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
|
||||||
then file_or_folder='file';
|
then file_or_folder='file';
|
||||||
else if index(fmsg, 'Insufficient authorization') then file_or_folder='file';
|
else if index(fmsg, 'Insufficient authorization') then file_or_folder='file';
|
||||||
else if file_or_folder='' then file_or_folder='locked';
|
else if file_or_folder='' then file_or_folder='locked';
|
||||||
|
|
||||||
if file_or_folder='file' then do;
|
if file_or_folder='file' then do;
|
||||||
ext = prxchange('s/.*\.{1,1}(.*)/$1/', 1, filename);
|
ext = prxchange('s/.*\.{1,1}(.*)/$1/', 1, filename);
|
||||||
if filename = ext then ext = ' ';
|
if filename = ext then ext = ' ';
|
||||||
@@ -96,7 +109,6 @@ data &outds (compress=no keep=file_or_folder filepath filename ext msg);
|
|||||||
ext='';
|
ext='';
|
||||||
file_or_folder='folder';
|
file_or_folder='folder';
|
||||||
end;
|
end;
|
||||||
filepath="&path/"!!filename;
|
|
||||||
output;
|
output;
|
||||||
end;
|
end;
|
||||||
rc = dclose(did);
|
rc = dclose(did);
|
||||||
@@ -120,7 +132,7 @@ run;
|
|||||||
else do i=1 to foptnum(fid);
|
else do i=1 to foptnum(fid);
|
||||||
infoname=foptname(fid,i);
|
infoname=foptname(fid,i);
|
||||||
infoval=finfo(fid,infoname);
|
infoval=finfo(fid,infoname);
|
||||||
sasname=compress(infoname, '_', 'adik');
|
sasname=compress(infoname, '_', 'adik');
|
||||||
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
|
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
|
||||||
if upcase(sasname) ne 'FILENAME' then output;
|
if upcase(sasname) ne 'FILENAME' then output;
|
||||||
end;
|
end;
|
||||||
@@ -137,7 +149,7 @@ run;
|
|||||||
else do i=1 to doptnum(fid);
|
else do i=1 to doptnum(fid);
|
||||||
infoname=doptname(fid,i);
|
infoname=doptname(fid,i);
|
||||||
infoval=dinfo(fid,infoname);
|
infoval=dinfo(fid,infoname);
|
||||||
sasname=compress(infoname, '_', 'adik');
|
sasname=compress(infoname, '_', 'adik');
|
||||||
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
|
if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
|
||||||
if upcase(sasname) ne 'FILENAME' then output;
|
if upcase(sasname) ne 'FILENAME' then output;
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
create view view2 as select * from sashelp.class;
|
create view view2 as select * from sashelp.class;
|
||||||
%mp_dropmembers(list=data1 view2)
|
%mp_dropmembers(list=data1 view2)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_isblank.sas
|
@li mf_isblank.sas
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -103,8 +103,7 @@ proc sql
|
|||||||
order by ranuni(42)
|
order by ranuni(42)
|
||||||
%end;
|
%end;
|
||||||
;
|
;
|
||||||
|
reset outobs=max;
|
||||||
|
|
||||||
create table datalines1 as
|
create table datalines1 as
|
||||||
select name,type,length,varnum,format,label from dictionary.columns
|
select name,type,length,varnum,format,label from dictionary.columns
|
||||||
where libname="%upcase(%scan(&base_ds,1))"
|
where libname="%upcase(%scan(&base_ds,1))"
|
||||||
|
|||||||
58
base/mp_ds2csv.sas
Normal file
58
base/mp_ds2csv.sas
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Export a dataset to a CSV file
|
||||||
|
@details Export to a file or a fileref
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
|
||||||
|
|
||||||
|
@param ds The dataset to be exported
|
||||||
|
@param outfile= The output filename - should be quoted.
|
||||||
|
@param outref= The output fileref (takes precedence if provided)
|
||||||
|
@param outencoding= The output encoding to use (unquoted)
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe (credit mjsq)
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_ds2csv(ds, outref=0, outfile=, outencoding=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if not %sysfunc(exist(&ds)) %then %do;
|
||||||
|
%put WARNING: &ds does not exist;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
|
||||||
|
|
||||||
|
%if &outencoding=0 %then %let outencoding=;
|
||||||
|
%else %let outencoding=encoding="&outencoding";
|
||||||
|
|
||||||
|
%local outloc;
|
||||||
|
%if &outref=0 %then %let outloc=&outfile;
|
||||||
|
%else %let outloc=&outref;
|
||||||
|
|
||||||
|
/* credit to mjsq - https://stackoverflow.com/a/55642267 */
|
||||||
|
|
||||||
|
/* first get headers */
|
||||||
|
data _null_;
|
||||||
|
file &outloc dlm=',' dsd &outencoding lrecl=32767;
|
||||||
|
length header $ 2000;
|
||||||
|
dsid=open("&ds.","i");
|
||||||
|
num=attrn(dsid,"nvars");
|
||||||
|
do i=1 to num;
|
||||||
|
header = trim(left(coalescec(varlabel(dsid,i),varname(dsid,i))));
|
||||||
|
put header @;
|
||||||
|
end;
|
||||||
|
rc=close(dsid);
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* next, export data */
|
||||||
|
data _null_;
|
||||||
|
set &ds.;
|
||||||
|
file &outloc mod dlm=',' dsd &outencoding lrecl=32767;
|
||||||
|
put (_all_) (+0);
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
@param ds= The target dataset. Leave blank (default) for all datasets.
|
@param ds= The target dataset. Leave blank (default) for all datasets.
|
||||||
@param outds the output dataset
|
@param outds the output dataset
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
326
base/mp_getdbml.sas
Normal file
326
base/mp_getdbml.sas
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Extract DBML from SAS Libraries
|
||||||
|
@details DBML is an open source markup format to represent databases.
|
||||||
|
More details: https://www.dbml.org/home/
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
|
||||||
|
%mp_getdbml(liblist=SASHELP WORK,outref=mydbml,showlog=YES)
|
||||||
|
|
||||||
|
Take the log output and paste it into the renderer at https://dbdiagram.io
|
||||||
|
to view your data model diagram. The code takes a "best guess" at
|
||||||
|
the one to one and one to many relationships (based on constraints
|
||||||
|
and indexes, and assuming that the column names would match).
|
||||||
|
|
||||||
|
You may need to adjust the rendered DBML to suit your needs.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getquotedstr.sas
|
||||||
|
@li mp_getconstraints.sas
|
||||||
|
|
||||||
|
@param liblist= Space seperated list of librefs to take as
|
||||||
|
input (Default=SASHELP)
|
||||||
|
@param outref= Fileref to contain the DBML (Default=getdbml)
|
||||||
|
@param showlog= set to YES to show the DBML in the log (Default is NO)
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author Allan Bowe
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_getdbml(liblist=SASHELP,outref=getdbml,showlog=NO
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
/* check fileref is assigned */
|
||||||
|
%if %sysfunc(fileref(&outref)) > 0 %then %do;
|
||||||
|
filename &outref temp;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%let liblist=%upcase(&liblist);
|
||||||
|
|
||||||
|
proc sql noprint;
|
||||||
|
create table _data_ as
|
||||||
|
select * from dictionary.tables
|
||||||
|
where upcase(libname) in (%mf_getquotedstr(&liblist))
|
||||||
|
order by libname,memname;
|
||||||
|
%local tabinfo; %let tabinfo=&syslast;
|
||||||
|
|
||||||
|
create table _data_ as
|
||||||
|
select * from dictionary.columns
|
||||||
|
where upcase(libname) in (%mf_getquotedstr(&liblist))
|
||||||
|
order by libname,memname,varnum;
|
||||||
|
%local colinfo; %let colinfo=&syslast;
|
||||||
|
|
||||||
|
%local dsnlist;
|
||||||
|
select distinct upcase(cats(libname,'.',memname)) into: dsnlist
|
||||||
|
separated by ' '
|
||||||
|
from &syslast
|
||||||
|
;
|
||||||
|
|
||||||
|
create table _data_ as
|
||||||
|
select * from dictionary.indexes
|
||||||
|
where upcase(libname) in (%mf_getquotedstr(&liblist))
|
||||||
|
order by idxusage, indxname, indxpos;
|
||||||
|
%local idxinfo; %let idxinfo=&syslast;
|
||||||
|
|
||||||
|
/* Extract all Primary Key and Unique data constraints */
|
||||||
|
%mp_getconstraints(lib=%scan(&liblist,1),outds=_data_)
|
||||||
|
%local colconst; %let colconst=&syslast;
|
||||||
|
|
||||||
|
%do x=2 %to %sysfunc(countw(&liblist));
|
||||||
|
%mp_getconstraints(lib=%scan(&liblist,&x),outds=_data_)
|
||||||
|
proc append base=&colconst data=&syslast;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* header info */
|
||||||
|
data _null_;
|
||||||
|
file &outref;
|
||||||
|
put "// DBML generated by &sysuserid on %sysfunc(datetime(),datetime19.) ";
|
||||||
|
put "Project sasdbml {";
|
||||||
|
put " database_type: 'SAS'";
|
||||||
|
put " Note: 'Generated by the mp_getdbml() macro'";
|
||||||
|
put "}";
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* create table groups */
|
||||||
|
data _null_;
|
||||||
|
file &outref mod;
|
||||||
|
set &tabinfo;
|
||||||
|
by libname;
|
||||||
|
if first.libname then put "TableGroup " libname "{";
|
||||||
|
ds=quote(cats(libname,'.',memname));
|
||||||
|
put ' ' ds;
|
||||||
|
if last.libname then put "}";
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* table for pks */
|
||||||
|
data _data_;
|
||||||
|
length curds const col $39;
|
||||||
|
call missing (of _all_);
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
%let pkds=&syslast;
|
||||||
|
|
||||||
|
%local x curds constraints_used constcheck;
|
||||||
|
%do x=1 %to %sysfunc(countw(&dsnlist,%str( )));
|
||||||
|
%let curds=%scan(&dsnlist,&x,%str( ));
|
||||||
|
%let constraints_used=;
|
||||||
|
%let constcheck=0;
|
||||||
|
data _null_;
|
||||||
|
file &outref mod;
|
||||||
|
length lab $1024 typ $20;
|
||||||
|
set &colinfo (where=(
|
||||||
|
libname="%scan(&curds,1,.)" and upcase(memname)="%scan(&curds,2,.)"
|
||||||
|
)) end=last;
|
||||||
|
|
||||||
|
if _n_=1 then do;
|
||||||
|
table='Table "'!!"&curds"!!'"{';
|
||||||
|
put table;
|
||||||
|
end;
|
||||||
|
name=upcase(name);
|
||||||
|
lab=" note:"!!quote(trim(tranwrd(label,'"',"'")));
|
||||||
|
if upcase(format)=:'DATETIME' then typ='datetime';
|
||||||
|
else if type='char' then typ=cats('char(',length,')');
|
||||||
|
else typ='num';
|
||||||
|
|
||||||
|
if notnull='yes' then notnul=' not null';
|
||||||
|
if notnull='no' and missing(label) then put ' ' name typ;
|
||||||
|
else if notnull='yes' and missing(label) then put ' ' name typ '[' notnul ']';
|
||||||
|
else if notnull='no' then put ' ' name typ '[' lab ']';
|
||||||
|
else put ' ' name typ '[' notnul ',' lab ']';
|
||||||
|
|
||||||
|
run;
|
||||||
|
|
||||||
|
data _data_(keep=curds const col);
|
||||||
|
length ctype $11 cols constraints_used $5000;
|
||||||
|
set &colconst (where=(
|
||||||
|
upcase(libref)="%scan(&curds,1,.)"
|
||||||
|
and upcase(table_name)="%scan(&curds,2,.)"
|
||||||
|
and constraint_type in ('PRIMARY','UNIQUE')
|
||||||
|
)) end=last;
|
||||||
|
file &outref mod;
|
||||||
|
by constraint_type constraint_name;
|
||||||
|
retain cols;
|
||||||
|
column_name=upcase(column_name);
|
||||||
|
|
||||||
|
if _n_=1 then put / ' indexes {';
|
||||||
|
|
||||||
|
if upcase(strip(constraint_type)) = 'PRIMARY' then ctype='[pk]';
|
||||||
|
else ctype='[unique]';
|
||||||
|
|
||||||
|
if first.constraint_name then cols = cats('(',column_name);
|
||||||
|
else cols=cats(cols,',',column_name);
|
||||||
|
|
||||||
|
if last.constraint_name then do;
|
||||||
|
cols=cats(cols,')',ctype)!!' //'!!constraint_name;
|
||||||
|
put ' ' cols;
|
||||||
|
constraints_used=catx(' ',constraints_used, constraint_name);
|
||||||
|
call symputx('constcheck',1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if last then call symputx('constraints_used',cats(upcase(constraints_used)));
|
||||||
|
|
||||||
|
length curds const col $39;
|
||||||
|
curds="&curds";
|
||||||
|
const=constraint_name;
|
||||||
|
col=column_name;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc append base=&pkds data=&syslast;run;
|
||||||
|
|
||||||
|
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
||||||
|
data _data_(keep=curds const col);
|
||||||
|
set &idxinfo (where=(
|
||||||
|
libname="%scan(&curds,1,.)"
|
||||||
|
and upcase(memname)="%scan(&curds,2,.)"
|
||||||
|
and unique='yes'
|
||||||
|
and upcase(indxname) not in (%mf_getquotedstr(&constraints_used))
|
||||||
|
));
|
||||||
|
file &outref mod;
|
||||||
|
by idxusage indxname;
|
||||||
|
name=upcase(name);
|
||||||
|
if &constcheck=1 then stop; /* in fact we only care about PKs so stop if we have */
|
||||||
|
if _n_=1 and &constcheck=0 then put / ' indexes {';
|
||||||
|
|
||||||
|
length cols $5000;
|
||||||
|
retain cols;
|
||||||
|
if first.indxname then cols = cats('(',name);
|
||||||
|
else cols=cats(cols,',',name);
|
||||||
|
|
||||||
|
if last.indxname then do;
|
||||||
|
cols=cats(cols,')[unique]')!!' //'!!indxname;
|
||||||
|
put ' ' cols;
|
||||||
|
call symputx('constcheck',1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
length curds const col $39;
|
||||||
|
curds="&curds";
|
||||||
|
const=indxname;
|
||||||
|
col=name;
|
||||||
|
run;
|
||||||
|
proc append base=&pkds data=&syslast;run;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &outref mod;
|
||||||
|
if &constcheck =1 then put ' }';
|
||||||
|
put '}';
|
||||||
|
run;
|
||||||
|
|
||||||
|
%end;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* now we need to figure out the relationships
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* sort alphabetically so we can have one set of unique cols per table */
|
||||||
|
proc sort data=&pkds nodupkey;
|
||||||
|
by curds const col;
|
||||||
|
run;
|
||||||
|
|
||||||
|
data &pkds.1 (keep=curds col)
|
||||||
|
&pkds.2 (keep=curds cols);
|
||||||
|
set &pkds;
|
||||||
|
by curds const;
|
||||||
|
length retconst $39 cols $5000;
|
||||||
|
retain retconst cols;
|
||||||
|
if first.curds then do;
|
||||||
|
retconst=const;
|
||||||
|
cols=upcase(col);
|
||||||
|
end;
|
||||||
|
else cols=catx(' ',cols,upcase(col));
|
||||||
|
if retconst=const then do;
|
||||||
|
output &pkds.1;
|
||||||
|
if last.const then output &pkds.2;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%let curdslist="0";
|
||||||
|
%do x=1 %to %sysfunc(countw(&dsnlist,%str( )));
|
||||||
|
%let curds=%scan(&dsnlist,&x,%str( ));
|
||||||
|
|
||||||
|
%let pkcols=0;
|
||||||
|
data _null_;
|
||||||
|
set &pkds.2(where=(curds="&curds"));
|
||||||
|
call symputx('pkcols',cols);
|
||||||
|
run;
|
||||||
|
%if &pkcols ne 0 %then %do;
|
||||||
|
%let curdslist=&curdslist,"&curds";
|
||||||
|
|
||||||
|
/* start with one2one */
|
||||||
|
data &pkds.4;
|
||||||
|
file &outref mod;
|
||||||
|
set &pkds.2(where=(cols="&pkcols" and curds not in (&curdslist)));
|
||||||
|
line='Ref: "'!!"&curds"
|
||||||
|
!!cats('".(',"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))",')')
|
||||||
|
!!' - '
|
||||||
|
!!cats(quote(trim(curds)),'.(',"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))",')');
|
||||||
|
put line;
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* now many2one */
|
||||||
|
/* get table with one row per col */
|
||||||
|
data &pkds.5;
|
||||||
|
set &pkds.1(where=(curds="&curds"));
|
||||||
|
run;
|
||||||
|
/* get tables which contain the PK columns */
|
||||||
|
proc sql;
|
||||||
|
create table &pkds.5a as
|
||||||
|
select upcase(cats(b.libname,'.',b.memname)) as curds
|
||||||
|
,b.name
|
||||||
|
from &pkds.5 a
|
||||||
|
inner join &colinfo b
|
||||||
|
on a.col=upcase(b.name);
|
||||||
|
/* count to make sure those tables contain ALL the columns */
|
||||||
|
create table &pkds.5b as
|
||||||
|
select curds,count(*) as cnt
|
||||||
|
from &pkds.5a
|
||||||
|
where curds not in (select curds from &pkds.2 where cols="&pkcols") /* not a one to one match */
|
||||||
|
and curds ne "&curds" /* exclude self */
|
||||||
|
group by 1;
|
||||||
|
create table &pkds.6 as
|
||||||
|
select a.*
|
||||||
|
,b.cols
|
||||||
|
from &pkds.5b a
|
||||||
|
left join &pkds.4 b
|
||||||
|
on a.curds=b.curds;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set &pkds.6;
|
||||||
|
file &outref mod;
|
||||||
|
colcnt=%sysfunc(countw(&pkcols));
|
||||||
|
if cnt=colcnt then do;
|
||||||
|
/* table contains all the PK cols, and was not a direct / 121 match */
|
||||||
|
line='Ref: "'!!"&curds"
|
||||||
|
!!'".('
|
||||||
|
!!"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))"
|
||||||
|
!!') > '
|
||||||
|
!!cats(quote(trim(curds))
|
||||||
|
,'.('
|
||||||
|
,"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))"
|
||||||
|
,')'
|
||||||
|
);
|
||||||
|
put line;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
|
||||||
|
%if %upcase(&showlog)=YES %then %do;
|
||||||
|
options ps=max;
|
||||||
|
data _null_;
|
||||||
|
infile &outref;
|
||||||
|
input;
|
||||||
|
putlog _infile_;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -13,11 +13,13 @@
|
|||||||
label x='blah';
|
label x='blah';
|
||||||
run;
|
run;
|
||||||
proc sql; describe table &syslast;
|
proc sql; describe table &syslast;
|
||||||
|
|
||||||
%mp_getddl(work,test,flavour=tsql,showlog=YES)
|
%mp_getddl(work,test,flavour=tsql,showlog=YES)
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_getconstraints.sas
|
||||||
|
|
||||||
@param lib libref of the library to create DDL for. Should be assigned.
|
@param lib libref of the library to create DDL for. Should be assigned.
|
||||||
@param ds dataset to create ddl for
|
@param ds dataset to create ddl for (optional)
|
||||||
@param fref= the fileref to which to write the DDL. If not preassigned, will
|
@param fref= the fileref to which to write the DDL. If not preassigned, will
|
||||||
be assigned to TEMP.
|
be assigned to TEMP.
|
||||||
@param flavour= The type of DDL to create (default=SAS). Supported=TSQL
|
@param flavour= The type of DDL to create (default=SAS). Supported=TSQL
|
||||||
@@ -26,11 +28,8 @@
|
|||||||
,else libref)
|
,else libref)
|
||||||
@param applydttm= for non SAS DDL, choose if columns are created with native
|
@param applydttm= for non SAS DDL, choose if columns are created with native
|
||||||
datetime2 format or regular decimal type
|
datetime2 format or regular decimal type
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_getddl(libref,ds,fref=getddl,flavour=SAS,showlog=NO,schema=
|
%macro mp_getddl(libref,ds,fref=getddl,flavour=SAS,showlog=NO,schema=
|
||||||
@@ -54,6 +53,21 @@ create table _data_ as
|
|||||||
;
|
;
|
||||||
%local tabinfo; %let tabinfo=&syslast;
|
%local tabinfo; %let tabinfo=&syslast;
|
||||||
|
|
||||||
|
create table _data_ as
|
||||||
|
select * from dictionary.columns
|
||||||
|
where upcase(libname)="%upcase(&libref)"
|
||||||
|
%if %length(&ds)>0 %then %do;
|
||||||
|
and upcase(memname)="%upcase(&ds)"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
%local colinfo; %let colinfo=&syslast;
|
||||||
|
|
||||||
|
%local dsnlist;
|
||||||
|
select distinct upcase(memname) into: dsnlist
|
||||||
|
separated by ' '
|
||||||
|
from &syslast
|
||||||
|
;
|
||||||
|
|
||||||
create table _data_ as
|
create table _data_ as
|
||||||
select * from dictionary.indexes
|
select * from dictionary.indexes
|
||||||
where upcase(libname)="%upcase(&libref)"
|
where upcase(libname)="%upcase(&libref)"
|
||||||
@@ -64,18 +78,43 @@ create table _data_ as
|
|||||||
;
|
;
|
||||||
%local idxinfo; %let idxinfo=&syslast;
|
%local idxinfo; %let idxinfo=&syslast;
|
||||||
|
|
||||||
create table _data_ as
|
/* Extract all Primary Key and Unique data constraints */
|
||||||
select * from dictionary.columns
|
%mp_getconstraints(lib=%upcase(&libref),ds=%upcase(&ds),outds=_data_)
|
||||||
where upcase(libname)="%upcase(&libref)"
|
%local colconst; %let colconst=&syslast;
|
||||||
%if %length(&ds)>0 %then %do;
|
|
||||||
and upcase(memname)="%upcase(&ds)"
|
%macro addConst();
|
||||||
%end;
|
%global constraints_used;
|
||||||
;
|
data _null_;
|
||||||
%local colinfo; %let colinfo=&syslast;
|
length ctype $11 constraint_name_orig $256 constraints_used $5000;
|
||||||
%local dsnlist;
|
set &colconst (where=(table_name="&curds" and constraint_type in ('PRIMARY','UNIQUE'))) end=last;
|
||||||
select distinct upcase(memname) into: dsnlist
|
file &fref mod;
|
||||||
separated by ' '
|
by constraint_type constraint_name;
|
||||||
from &syslast;
|
retain constraints_used;
|
||||||
|
constraint_name_orig=constraint_name;
|
||||||
|
if upcase(strip(constraint_type)) = 'PRIMARY' then ctype='PRIMARY KEY';
|
||||||
|
else ctype=strip(constraint_type);
|
||||||
|
%if &flavour=TSQL %then %do;
|
||||||
|
column_name=catt('[',column_name,']');
|
||||||
|
constraint_name=catt('[',constraint_name,']');
|
||||||
|
%end;
|
||||||
|
%else %if &flavour=PGSQL %then %do;
|
||||||
|
column_name=catt('"',column_name,'"');
|
||||||
|
constraint_name=catt('"',constraint_name,'"');
|
||||||
|
%end;
|
||||||
|
if first.constraint_name then do;
|
||||||
|
constraints_used = catx(' ', constraints_used, constraint_name_orig);
|
||||||
|
put " ,CONSTRAINT " constraint_name ctype "(" ;
|
||||||
|
put ' ' column_name;
|
||||||
|
end;
|
||||||
|
else put ' ,' column_name;
|
||||||
|
if last.constraint_name then do;
|
||||||
|
put " )";
|
||||||
|
call symput('constraints_used',strip(constraints_used));
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
%put &=constraints_used;
|
||||||
|
%mend;
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref;
|
file &fref;
|
||||||
put "/* DDL generated by &sysuserid on %sysfunc(datetime(),datetime19.) */";
|
put "/* DDL generated by &sysuserid on %sysfunc(datetime(),datetime19.) */";
|
||||||
@@ -84,14 +123,15 @@ run;
|
|||||||
%local x curds;
|
%local x curds;
|
||||||
%if &flavour=SAS %then %do;
|
%if &flavour=SAS %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref;
|
file &fref mod;
|
||||||
|
put "/* SAS Flavour DDL for %upcase(&libref).&curds */";
|
||||||
put "proc sql;";
|
put "proc sql;";
|
||||||
run;
|
run;
|
||||||
%do x=1 %to %sysfunc(countw(&dsnlist));
|
%do x=1 %to %sysfunc(countw(&dsnlist));
|
||||||
%let curds=%scan(&dsnlist,&x);
|
%let curds=%scan(&dsnlist,&x);
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
length nm lab $1024;
|
length nm lab $1024 typ $20;
|
||||||
set &colinfo (where=(upcase(memname)="&curds")) end=last;
|
set &colinfo (where=(upcase(memname)="&curds")) end=last;
|
||||||
|
|
||||||
if _n_=1 then do;
|
if _n_=1 then do;
|
||||||
@@ -105,27 +145,40 @@ run;
|
|||||||
end;
|
end;
|
||||||
else put " ,"@@;
|
else put " ,"@@;
|
||||||
if length(format)>1 then fmt=" format="!!cats(format);
|
if length(format)>1 then fmt=" format="!!cats(format);
|
||||||
len=" length="!!cats(length);
|
if length(label)>1 then lab=" label="!!quote(trim(label));
|
||||||
lab=" label="!!quote(trim(label));
|
|
||||||
if notnull='yes' then notnul=' not null';
|
if notnull='yes' then notnul=' not null';
|
||||||
put name type len fmt notnul lab;
|
if type='char' then typ=cats('char(',length,')');
|
||||||
if last then put ');';
|
else if length ne 8 then typ='num length='!!left(length);
|
||||||
|
else typ='num';
|
||||||
|
put name typ fmt notnul lab;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
/* Extra step for data constraints */
|
||||||
|
%addConst()
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
length ds $128;
|
file &fref mod;
|
||||||
set &idxinfo (where=(memname="&curds")) end=last;
|
put ');';
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
||||||
|
data _null_;
|
||||||
|
*length ds $128;
|
||||||
|
set &idxinfo (where=(memname="&curds" and unique='yes' and indxname not in (%sysfunc(tranwrd("&constraints_used",%str( ),%str(","))))));
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
by idxusage indxname;
|
by idxusage indxname;
|
||||||
if unique='yes' then uniq=' unique';
|
/* ds=cats(libname,'.',memname); */
|
||||||
ds=cats(libname,'.',memname);
|
|
||||||
if first.indxname then do;
|
if first.indxname then do;
|
||||||
put 'create ' uniq ' index ' indxname;
|
put 'CREATE UNIQUE INDEX ' indxname "ON &libref..&curds (" ;
|
||||||
put ' on ' ds '(' name @@;
|
put ' ' name ;
|
||||||
|
end;
|
||||||
|
else put ' ,' name ;
|
||||||
|
*else put ' ,' name ;
|
||||||
|
if last.indxname then do;
|
||||||
|
put ');';
|
||||||
end;
|
end;
|
||||||
else put ',' name @@;
|
|
||||||
if last.indxname then put ');';
|
|
||||||
run;
|
run;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ods output IntegrityConstraints=ic;
|
ods output IntegrityConstraints=ic;
|
||||||
proc contents data=testali out2=info;
|
proc contents data=testali out2=info;
|
||||||
@@ -146,7 +199,7 @@ run;
|
|||||||
%let curds=%scan(&dsnlist,&x);
|
%let curds=%scan(&dsnlist,&x);
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
put "/* DDL for &schema..&curds */";
|
put "/* TSQL Flavour DDL for &schema..&curds */";
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
set &colinfo (where=(upcase(memname)="&curds")) end=last;
|
set &colinfo (where=(upcase(memname)="&curds")) end=last;
|
||||||
@@ -169,31 +222,30 @@ run;
|
|||||||
else if length le 8000 then fmt='[varchar]('!!cats(length)!!')';
|
else if length le 8000 then fmt='[varchar]('!!cats(length)!!')';
|
||||||
else fmt=cats('[varchar](max)');
|
else fmt=cats('[varchar](max)');
|
||||||
if notnull='yes' then notnul=' NOT NULL';
|
if notnull='yes' then notnul=' NOT NULL';
|
||||||
put name fmt notnul;
|
put "[" name +(-1) "]" fmt notnul;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
/* Extra step for data constraints */
|
||||||
|
%addConst()
|
||||||
|
|
||||||
|
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
||||||
data _null_;
|
data _null_;
|
||||||
length ds $128;
|
*length ds $128;
|
||||||
set &idxinfo (where=(memname="&curds"));
|
set &idxinfo (where=(memname="&curds" and unique='yes' and indxname not in (%sysfunc(tranwrd("&constraints_used",%str( ),%str(","))))));
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
by idxusage indxname;
|
by idxusage indxname;
|
||||||
if unique='yes' then uniq=' unique';
|
*ds=cats(libname,'.',memname);
|
||||||
ds=cats(libname,'.',memname);
|
|
||||||
if first.indxname then do;
|
if first.indxname then do;
|
||||||
if unique='yes' and nomiss='yes' then do;
|
/* add nonclustered in case of multiple unique indexes */
|
||||||
put ' ,constraint [' indxname '] PRIMARY KEY';
|
put ' ,index [' indxname +(-1) '] UNIQUE NONCLUSTERED (';
|
||||||
end;
|
put ' [' name +(-1) ']';
|
||||||
else if unique='yes' then do;
|
|
||||||
/* add nonclustered in case of multiple unique indexes */
|
|
||||||
put ' ,index [' indxname '] UNIQUE NONCLUSTERED';
|
|
||||||
end;
|
|
||||||
put ' (';
|
|
||||||
put ' [' name ']';
|
|
||||||
end;
|
end;
|
||||||
else put ' ,[' name ']';
|
else put ' ,[' name +(-1) ']';
|
||||||
if last.indxname then do;
|
if last.indxname then do;
|
||||||
put ' )';
|
put ' )';
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
put ')';
|
put ')';
|
||||||
@@ -217,8 +269,78 @@ run;
|
|||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
%else %if &flavour=PGSQL %then %do;
|
||||||
|
/* if schema does not exist, set to be same as libref */
|
||||||
|
%local schemaactual;
|
||||||
|
proc sql noprint;
|
||||||
|
select sysvalue into: schemaactual
|
||||||
|
from dictionary.libnames
|
||||||
|
where libname="&libref" and engine='POSTGRES';
|
||||||
|
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
||||||
|
data _null_;
|
||||||
|
file &fref mod;
|
||||||
|
put "CREATE SCHEMA &schema;";
|
||||||
|
%do x=1 %to %sysfunc(countw(&dsnlist));
|
||||||
|
%let curds=%scan(&dsnlist,&x);
|
||||||
|
data _null_;
|
||||||
|
file &fref mod;
|
||||||
|
put "/* Postgres Flavour DDL for &schema..&curds */";
|
||||||
|
data _null_;
|
||||||
|
file &fref mod;
|
||||||
|
set &colinfo (where=(upcase(memname)="&curds")) end=last;
|
||||||
|
length fmt $32;
|
||||||
|
if _n_=1 then do;
|
||||||
|
if memtype='DATA' then do;
|
||||||
|
put "CREATE TABLE &schema..&curds (";
|
||||||
|
end;
|
||||||
|
else do;
|
||||||
|
put "CREATE VIEW &schema..&curds (";
|
||||||
|
end;
|
||||||
|
put " "@@;
|
||||||
|
end;
|
||||||
|
else put " ,"@@;
|
||||||
|
format=upcase(format);
|
||||||
|
if 1=0 then; /* dummy if */
|
||||||
|
%if &applydttm=YES %then %do;
|
||||||
|
else if format=:'DATETIME' then fmt=' TIMESTAMP ';
|
||||||
|
%end;
|
||||||
|
else if type='num' then fmt=' DOUBLE PRECISION';
|
||||||
|
else fmt='VARCHAR('!!cats(length)!!')';
|
||||||
|
if notnull='yes' then notnul=' NOT NULL';
|
||||||
|
/* quote column names in case they represent reserved words */
|
||||||
|
name2=quote(trim(name));
|
||||||
|
put name2 fmt notnul;
|
||||||
|
run;
|
||||||
|
|
||||||
%if &showlog=YES %then %do;
|
/* Extra step for data constraints */
|
||||||
|
%addConst()
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file &fref mod;
|
||||||
|
put ');';
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* Create Unique Indexes, but only if they were not already defined within the Constraints section. */
|
||||||
|
data _null_;
|
||||||
|
*length ds $128;
|
||||||
|
set &idxinfo (where=(memname="&curds" and unique='yes' and indxname not in (%sysfunc(tranwrd("&constraints_used",%str( ),%str(","))))));
|
||||||
|
file &fref mod;
|
||||||
|
by idxusage indxname;
|
||||||
|
/* ds=cats(libname,'.',memname); */
|
||||||
|
if first.indxname then do;
|
||||||
|
put 'CREATE UNIQUE INDEX "' indxname +(-1) '" ' "ON &schema..&curds (" ;
|
||||||
|
put ' "' name +(-1) '"' ;
|
||||||
|
end;
|
||||||
|
else put ' ,"' name +(-1) '"';
|
||||||
|
*else put ' ,' name ;
|
||||||
|
if last.indxname then do;
|
||||||
|
put ');';
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
%if %upcase(&showlog)=YES %then %do;
|
||||||
options ps=max;
|
options ps=max;
|
||||||
data _null_;
|
data _null_;
|
||||||
infile &fref;
|
infile &fref;
|
||||||
@@ -227,4 +349,4 @@ run;
|
|||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%mend;
|
%mend;
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
@param libds Two part dataset (or view) reference.
|
@param libds Two part dataset (or view) reference.
|
||||||
@param outds= The output dataset to create
|
@param outds= The output dataset to create
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getvarlist.sas
|
@li mf_getvarlist.sas
|
||||||
@li mf_getvartype.sas
|
@li mf_getvartype.sas
|
||||||
@li mf_getvarformat.sas
|
@li mf_getvarformat.sas
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
@brief Guess the primary key of a table
|
@brief Guess the primary key of a table
|
||||||
@details Tries to guess the primary key of a table based on the following logic:
|
@details Tries to guess the primary key of a table based on the following logic:
|
||||||
|
|
||||||
* Columns with nulls are ignored
|
* Columns with nulls are ignored
|
||||||
* Return only column combinations that provide unique results
|
* Return only column combinations that provide unique results
|
||||||
* Start from one column, then move out to include composite keys of 2 to 6 columns
|
* Start from one column, then move out to include composite keys of 2 to 6 columns
|
||||||
|
|
||||||
The library of the target should be assigned before using this macro.
|
The library of the target should be assigned before using this macro.
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
@param min_rows= The minimum number of rows a table should have in order to try
|
@param min_rows= The minimum number of rows a table should have in order to try
|
||||||
and guess the PK. Default=5.
|
and guess the PK. Default=5.
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getvarlist.sas
|
@li mf_getvarlist.sas
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
/* get null count and row count */
|
/* get null count and row count */
|
||||||
%let tmpvar=%mf_getuniquename();
|
%let tmpvar=%mf_getuniquename();
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
create table _data_ as select
|
create table _data_ as select
|
||||||
count(*) as &tmpvar
|
count(*) as &tmpvar
|
||||||
%do i=1 %to &vcnt;
|
%do i=1 %to &vcnt;
|
||||||
%let var=%scan(&vars,&i);
|
%let var=%scan(&vars,&i);
|
||||||
@@ -85,10 +85,10 @@
|
|||||||
%put &sysmacroname: &baseds has no combination of unique records! Exiting.;
|
%put &sysmacroname: &baseds has no combination of unique records! Exiting.;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
/* now check cardinality */
|
/* now check cardinality */
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
create table _data_ as select
|
create table _data_ as select
|
||||||
%do i=1 %to &ppkcnt;
|
%do i=1 %to &ppkcnt;
|
||||||
%let var=%scan(&posspks,&i);
|
%let var=%scan(&posspks,&i);
|
||||||
count(distinct &var) as &var
|
count(distinct &var) as &var
|
||||||
@@ -212,7 +212,7 @@
|
|||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%if &ppkcnt=4 %then %do;
|
%if &ppkcnt=4 %then %do;
|
||||||
%put &sysmacroname: No more PK guess possible;
|
%put &sysmacroname: No more PK guess possible;
|
||||||
%return;
|
%return;
|
||||||
@@ -228,7 +228,7 @@
|
|||||||
%let lev3=%scan(&posspks,&k);
|
%let lev3=%scan(&posspks,&k);
|
||||||
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
||||||
%let lev4=%scan(&posspks,&l);
|
%let lev4=%scan(&posspks,&l);
|
||||||
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
||||||
%do m=5 %to &ppkcnt;
|
%do m=5 %to &ppkcnt;
|
||||||
%let lev5=%scan(&posspks,&m);
|
%let lev5=%scan(&posspks,&m);
|
||||||
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then %do;
|
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then %do;
|
||||||
@@ -250,7 +250,7 @@
|
|||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%if &ppkcnt=5 %then %do;
|
%if &ppkcnt=5 %then %do;
|
||||||
%put &sysmacroname: No more PK guess possible;
|
%put &sysmacroname: No more PK guess possible;
|
||||||
%return;
|
%return;
|
||||||
@@ -266,17 +266,17 @@
|
|||||||
%let lev3=%scan(&posspks,&k);
|
%let lev3=%scan(&posspks,&k);
|
||||||
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
%if &lev1 ne &lev3 and &lev2 ne &lev3 %then %do l=4 %to &ppkcnt;
|
||||||
%let lev4=%scan(&posspks,&l);
|
%let lev4=%scan(&posspks,&l);
|
||||||
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
%if &lev1 ne &lev4 and &lev2 ne &lev4 and &lev3 ne &lev4 %then
|
||||||
%do m=5 %to &ppkcnt;
|
%do m=5 %to &ppkcnt;
|
||||||
%let lev5=%scan(&posspks,&m);
|
%let lev5=%scan(&posspks,&m);
|
||||||
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then
|
%if &lev1 ne &lev5 & &lev2 ne &lev5 & &lev3 ne &lev5 & &lev4 ne &lev5 %then
|
||||||
%do n=6 %to &ppkcnt;
|
%do n=6 %to &ppkcnt;
|
||||||
%let lev6=%scan(&posspks,&n);
|
%let lev6=%scan(&posspks,&n);
|
||||||
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
|
%if &lev1 ne &lev6 & &lev2 ne &lev6 & &lev3 ne &lev6
|
||||||
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
|
& &lev4 ne &lev6 & &lev5 ne &lev6 %then
|
||||||
%do;
|
%do;
|
||||||
/* check for four level uniqueness */
|
/* check for four level uniqueness */
|
||||||
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
|
proc sort data=&pkds(keep=&lev1 &lev2 &lev3 &lev4 &lev5 &lev6)
|
||||||
out=&tmpds noduprec;
|
out=&tmpds noduprec;
|
||||||
by _all_;
|
by _all_;
|
||||||
run;
|
run;
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%if &ppkcnt=6 %then %do;
|
%if &ppkcnt=6 %then %do;
|
||||||
%put &sysmacroname: No more PK guess possible;
|
%put &sysmacroname: No more PK guess possible;
|
||||||
%return;
|
%return;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
%mp_lib2cards(lib=sashelp
|
%mp_lib2cards(lib=sashelp
|
||||||
, outloc= C:\temp )
|
, outloc= C:\temp )
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_mkdir.sas
|
@li mf_mkdir.sas
|
||||||
@li mp_ds2cards.sas
|
@li mp_ds2cards.sas
|
||||||
|
|
||||||
|
|||||||
88
base/mp_prevobs.sas
Normal file
88
base/mp_prevobs.sas
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Enables previous observations to be re-instated
|
||||||
|
@details Remembers the last X observations by storing them in a hash table.
|
||||||
|
Is a convenience over the use of lag() or retain, when an entire observation
|
||||||
|
needs to be restored.
|
||||||
|
|
||||||
|
This macro will also restore automatic variables (such as _n_ and _error_).
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
|
||||||
|
data example;
|
||||||
|
set sashelp.class;
|
||||||
|
calc_var=_n_*3;
|
||||||
|
%* initialise hash and save from PDV ;
|
||||||
|
%mp_prevobs(INIT,history=2)
|
||||||
|
if _n_ =10 then do;
|
||||||
|
%* fetch previous but 1 record;
|
||||||
|
%mp_prevobs(FETCH,-2)
|
||||||
|
put _n_= name= age= calc_var=;
|
||||||
|
%* fetch previous record;
|
||||||
|
%mp_prevobs(FETCH,-1)
|
||||||
|
put _n_= name= age= calc_var=;
|
||||||
|
%* reinstate current record ;
|
||||||
|
%mp_prevobs(FETCH,0)
|
||||||
|
put _n_= name= age= calc_var=;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
<img src="https://imgur.com/PSjHoET.png" alt="mp_prevobs sas" width="400"/>
|
||||||
|
|
||||||
|
Credit is made to `data _null_` for authoring this very helpful paper:
|
||||||
|
https://www.lexjansen.com/pharmasug/2008/cc/CC08.pdf
|
||||||
|
|
||||||
|
@param action Either FETCH a current or previous record, or INITialise.
|
||||||
|
@param record The relative (to current) position of the previous observation
|
||||||
|
to return.
|
||||||
|
@param history= The number of records to retain in the hash table. Default=5
|
||||||
|
@param prefix= the prefix to give to the variables used to store the hash name
|
||||||
|
and index. Default=mp_prevobs
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_prevobs(action,record,history=5,prefix=mp_prevobs
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%let action=%upcase(&action);
|
||||||
|
%let prefix=%upcase(&prefix);
|
||||||
|
%let record=%eval((&record+0) * -1);
|
||||||
|
|
||||||
|
%if &action=INIT %then %do;
|
||||||
|
|
||||||
|
if _n_ eq 1 then do;
|
||||||
|
attrib &prefix._VAR length=$64;
|
||||||
|
dcl hash &prefix._HASH(ordered:'Y');
|
||||||
|
&prefix._KEY=0;
|
||||||
|
&prefix._HASH.defineKey("&prefix._KEY");
|
||||||
|
do while(1);
|
||||||
|
call vnext(&prefix._VAR);
|
||||||
|
if &prefix._VAR='' then leave;
|
||||||
|
if &prefix._VAR eq "&prefix._VAR" then continue;
|
||||||
|
else if &prefix._VAR eq "&prefix._KEY" then continue;
|
||||||
|
&prefix._HASH.defineData(&prefix._VAR);
|
||||||
|
end;
|
||||||
|
&prefix._HASH.defineDone();
|
||||||
|
end;
|
||||||
|
/* this part has to happen before FETCHing */
|
||||||
|
&prefix._KEY+1;
|
||||||
|
&prefix._rc=&prefix._HASH.add();
|
||||||
|
if &prefix._rc then putlog 'adding' &prefix._rc=;
|
||||||
|
%if &history>0 %then %do;
|
||||||
|
if &prefix._key>&history+1 then
|
||||||
|
&prefix._HASH.remove(key: &prefix._KEY - &history - 1);
|
||||||
|
if &prefix._rc then putlog 'removing' &prefix._rc=;
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
%else %if &action=FETCH %then %do;
|
||||||
|
if &record > &prefix._key then putlog "Not enough records in &Prefix._hash yet";
|
||||||
|
else &prefix._rc=&prefix._HASH.find(key: &prefix._KEY - &record);
|
||||||
|
if &prefix._rc then putlog &prefix._rc= " when fetching " &prefix._KEY=
|
||||||
|
"with record &record and " _n_=;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
@brief Searches all data in a library
|
@brief Searches all data in a library
|
||||||
@details
|
@details
|
||||||
Scans an entire library and creates a copy of any table
|
Scans an entire library and creates a copy of any table
|
||||||
containing a specific string or numeric value. Only
|
containing a specific string OR numeric value. Only
|
||||||
matching records are written out.
|
matching records are written out.
|
||||||
If both a string and numval are provided, the string
|
If both a string and numval are provided, the string
|
||||||
will take precedence.
|
will take precedence.
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
@param outobs= set to a positive integer to restrict the number of observations
|
@param outobs= set to a positive integer to restrict the number of observations
|
||||||
@param filter_text= add a (valid) filter clause to further filter the results
|
@param filter_text= add a (valid) filter clause to further filter the results
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getvarlist.sas
|
@li mf_getvarlist.sas
|
||||||
@li mf_getvartype.sas
|
@li mf_getvartype.sas
|
||||||
@li mf_mkdir.sas
|
@li mf_mkdir.sas
|
||||||
@@ -44,9 +44,13 @@
|
|||||||
,filter_text=%str(1=1)
|
,filter_text=%str(1=1)
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%local table_list table table_num table colnum col start_tm vars type coltype;
|
%local table_list table table_num table colnum col start_tm check_tm vars type coltype;
|
||||||
%put process began at %sysfunc(datetime(),datetime19.);
|
%put process began at %sysfunc(datetime(),datetime19.);
|
||||||
|
|
||||||
|
%if &syscc ge 4 %then %do;
|
||||||
|
%put %str(WAR)NING: SYSCC=&syscc on macro entry;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
%if &string = %then %let type=N;
|
%if &string = %then %let type=N;
|
||||||
%else %let type=C;
|
%else %let type=C;
|
||||||
@@ -78,6 +82,7 @@ proc sql
|
|||||||
%put NO COLUMNS IN &lib..&table! This will be skipped.;
|
%put NO COLUMNS IN &lib..&table! This will be skipped.;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
|
%let check_tm=%sysfunc(datetime());
|
||||||
/* build sql statement */
|
/* build sql statement */
|
||||||
create table mpsearch.&table as select * from &lib..&table
|
create table mpsearch.&table as select * from &lib..&table
|
||||||
where %unquote(&filter_text) and
|
where %unquote(&filter_text) and
|
||||||
@@ -88,14 +93,19 @@ proc sql
|
|||||||
%let coltype=%mf_getvartype(&lib..&table,&col);
|
%let coltype=%mf_getvartype(&lib..&table,&col);
|
||||||
%if &type=C and &coltype=C %then %do;
|
%if &type=C and &coltype=C %then %do;
|
||||||
/* if a char column, see if it contains the string */
|
/* if a char column, see if it contains the string */
|
||||||
or (&col ? "&string")
|
or ("&col"n ? "&string")
|
||||||
%end;
|
%end;
|
||||||
%else %if &type=N and &coltype=N %then %do;
|
%else %if &type=N and &coltype=N %then %do;
|
||||||
/* if numeric match exactly */
|
/* if numeric match exactly */
|
||||||
or (&col = &numval)
|
or ("&col"n = &numval)
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
);
|
);
|
||||||
|
%put Search query for &table took %sysevalf(%sysfunc(datetime())-&check_tm) seconds;
|
||||||
|
%if &sqlrc ne 0 %then %do;
|
||||||
|
%put %str(WAR)NING: SQLRC=&sqlrc when processing &table;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
%if %mf_nobs(mpsearch.&table)=0 %then %do;
|
%if %mf_nobs(mpsearch.&table)=0 %then %do;
|
||||||
drop table mpsearch.&table;
|
drop table mpsearch.&table;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
%mp_setkeyvalue(someindex,22,type=N)
|
%mp_setkeyvalue(someindex,22,type=N)
|
||||||
%mp_setkeyvalue(somenewindex,somevalue)
|
%mp_setkeyvalue(somenewindex,somevalue)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_existds.sas
|
@li mf_existds.sas
|
||||||
|
|
||||||
@param key Provide a key on which to perform the lookup
|
@param key Provide a key on which to perform the lookup
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
@file mp_streamfile.sas
|
@file
|
||||||
@brief Streams a file to _webout according to content type
|
@brief Streams a file to _webout according to content type
|
||||||
@details Will set headers using appropriate functions (SAS 9 vs Viya) and send
|
@details Will set headers using appropriate functions (SAS 9 vs Viya) and send
|
||||||
content as a binary stream.
|
content as a binary stream.
|
||||||
@@ -11,12 +11,13 @@
|
|||||||
|
|
||||||
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
||||||
|
|
||||||
<h4> Dependencies </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 contenttype= Either TEXT, ZIP, CSV, EXCEL (default TEXT)
|
||||||
@param inloc= /path/to/file.ext to be sent
|
@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
|
@param outname= the name of the file, as downloaded by the browser
|
||||||
|
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
%macro mp_streamfile(
|
%macro mp_streamfile(
|
||||||
contenttype=TEXT
|
contenttype=TEXT
|
||||||
,inloc=
|
,inloc=
|
||||||
|
,inref=0
|
||||||
,outname=
|
,outname=
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
@@ -42,11 +44,12 @@
|
|||||||
%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 _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
|
||||||
contenttype='application/zip'
|
contenttype='application/zip'
|
||||||
contentdisp="attachment; filename=&outname";
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &contentype=EXCEL %then %do;
|
%else %if &contentype=EXCEL %then %do;
|
||||||
|
/* suitable for XLS format */
|
||||||
%if &platform=SASMETA %then %do;
|
%if &platform=SASMETA %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
|
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
|
||||||
@@ -55,7 +58,20 @@
|
|||||||
%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 _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||||
contenttype='application/vnd.ms-excel'
|
contenttype='application/vnd.ms-excel'
|
||||||
|
contentdisp="attachment; filename=&outname";
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
%else %if &contentype=XLSX %then %do;
|
||||||
|
%if &platform=SASMETA %then %do;
|
||||||
|
data _null_;
|
||||||
|
rc=stpsrv_header('Content-type','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||||
|
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.xls'
|
||||||
|
contenttype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
contentdisp="attachment; filename=&outname";
|
contentdisp="attachment; filename=&outname";
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
@@ -88,7 +104,7 @@
|
|||||||
%else %if &contentype=HTML %then %do;
|
%else %if &contentype=HTML %then %do;
|
||||||
%if &platform=SASVIYA %then %do;
|
%if &platform=SASVIYA %then %do;
|
||||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||||
contenttype="text/html";
|
contenttype="text/html";
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
@@ -96,6 +112,11 @@
|
|||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
%if &inref ne 0 %then %do;
|
||||||
|
%mp_binarycopy(inref=&inref,outref=_webout)
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
||||||
|
%end;
|
||||||
|
|
||||||
%mend;
|
%mend;
|
||||||
92
base/mp_testjob.sas
Normal file
92
base/mp_testjob.sas
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Runs arbitrary code for a specified amount of time
|
||||||
|
@details Executes a series of procs and data steps to enable performance
|
||||||
|
testing of arbitrary jobs.
|
||||||
|
|
||||||
|
%mp_testjob(
|
||||||
|
duration=60*5
|
||||||
|
)
|
||||||
|
|
||||||
|
@param [in] duration= the time in seconds which the job should run for. Actual
|
||||||
|
time may vary, as the check is done in between steps. Default = 30 (seconds).
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquelibref.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_mkdir.sas
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_testjob(duration=30
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local lib dir ds1 ds2 ds3 start_tm i;
|
||||||
|
|
||||||
|
%let start_tm=%sysfunc(datetime());
|
||||||
|
%let duration=%sysevalf(&duration);
|
||||||
|
|
||||||
|
/* create a temporary library in WORK */
|
||||||
|
%let lib=%mf_getuniquelibref();
|
||||||
|
%let dir=%mf_getuniquename();
|
||||||
|
%mf_mkdir(%sysfunc(pathname(work))/&dir)
|
||||||
|
libname &lib "%sysfunc(pathname(work))/&dir";
|
||||||
|
|
||||||
|
/* loop through until time expires */
|
||||||
|
%let ds1=%mf_getuniquename();
|
||||||
|
%let ds2=%mf_getuniquename();
|
||||||
|
%let ds3=%mf_getuniquename();
|
||||||
|
%do i=0 %to 1;
|
||||||
|
|
||||||
|
/* create big dataset */
|
||||||
|
data &lib..&ds1(compress=no );
|
||||||
|
do x=1 to 1000000;
|
||||||
|
randnum0=ranuni(0)*3;
|
||||||
|
randnum1=ranuni(0)*2;
|
||||||
|
bigchar=repeat('A',300);
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
proc summary ;
|
||||||
|
class randnum0 randnum1;
|
||||||
|
output out=&lib..&ds2;
|
||||||
|
run;quit;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
/* add more data */
|
||||||
|
proc sql;
|
||||||
|
create table &lib..&ds3 as
|
||||||
|
select *, ranuni(0)*10 as randnum2
|
||||||
|
from &lib..&ds1
|
||||||
|
order by randnum1;
|
||||||
|
quit;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
proc sort data=&lib..&ds3;
|
||||||
|
by descending x;
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
/* wait 5 seconds */
|
||||||
|
data _null_;
|
||||||
|
call sleep(5,1);
|
||||||
|
run;
|
||||||
|
%if %sysevalf( (%sysfunc(datetime())-&start_tm)>&duration ) %then %goto gate;
|
||||||
|
|
||||||
|
%let i=0;
|
||||||
|
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%gate:
|
||||||
|
%put time is up!;
|
||||||
|
proc datasets lib=&lib kill;
|
||||||
|
run;
|
||||||
|
quit;
|
||||||
|
libname &lib clear;
|
||||||
|
|
||||||
|
|
||||||
|
%mend;
|
||||||
59
base/mp_testwritespeedlibrary.sas
Normal file
59
base/mp_testwritespeedlibrary.sas
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
@file mp_testwritespeedlibrary.sas
|
||||||
|
@brief Tests the write speed of a new table in a SAS library
|
||||||
|
@details Will create a new table of a certain size in an
|
||||||
|
existing SAS library. The table will have one column,
|
||||||
|
and will be subsequently deleted.
|
||||||
|
|
||||||
|
%mp_testwritespeedlibrary(
|
||||||
|
lib=work
|
||||||
|
,size=0.5
|
||||||
|
,outds=work.results
|
||||||
|
)
|
||||||
|
|
||||||
|
@param lib= (WORK) The library in which to create the table
|
||||||
|
@param size= (0.1) The size in GB of the table to create
|
||||||
|
@param outds= (WORK.RESULTS) The output dataset to be created.
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_existds.sas
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_testwritespeedlibrary(lib=WORK
|
||||||
|
,outds=work.results
|
||||||
|
,size=0.1
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local ds start;
|
||||||
|
|
||||||
|
/* find an unused, unique name for the new table */
|
||||||
|
%let ds=%mf_getuniquename();
|
||||||
|
%do %until(%mf_existds(&lib..&ds)=0);
|
||||||
|
%let ds=%mf_getuniquename();
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%let start=%sysfunc(datetime());
|
||||||
|
|
||||||
|
data &lib..&ds(compress=no keep=x);
|
||||||
|
header=128*1024;
|
||||||
|
size=(1073741824/8 * &size) - header;
|
||||||
|
do x=1 to size;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
drop table &lib..&ds;
|
||||||
|
|
||||||
|
data &outds;
|
||||||
|
lib="&lib";
|
||||||
|
start_dttm=put(&start,datetime19.);
|
||||||
|
end_dttm=put(datetime(),datetime19.);
|
||||||
|
duration_seconds=end_dttm-start_dttm;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mend;
|
||||||
68
base/mp_tree.sas
Normal file
68
base/mp_tree.sas
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Recursively scans a directory tree to get all subfolders and content
|
||||||
|
@details
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mp_tree(dir=/tmp, outds=work.tree)
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
|
||||||
|
* Roger Deangelis, https://communities.sas.com/t5/SAS-Programming/listing-all-files-within-a-directory-and-subdirectories/m-p/332616/highlight/true#M74887
|
||||||
|
* Tom, https://communities.sas.com/t5/SAS-Programming/listing-all-files-of-all-types-from-all-subdirectories/m-p/334113/highlight/true#M75419
|
||||||
|
|
||||||
|
|
||||||
|
@param dir= Directory to be scanned (default=/tmp)
|
||||||
|
@param outds= Dataset to create (default=work.mp_tree)
|
||||||
|
|
||||||
|
@returns outds contains the following variables:
|
||||||
|
|
||||||
|
- `dir`: a flag (1/0) to say whether it is a directory or not. This is not
|
||||||
|
reliable - folders that you do not have permission to open will be flagged
|
||||||
|
as directories.
|
||||||
|
- `ext`: file extension
|
||||||
|
- `filename`: file name
|
||||||
|
- `dirname`: directory name
|
||||||
|
- `fullpath`: directory + file name
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_tree(dir=/tmp
|
||||||
|
,outds=work.mp_tree
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
data &outds ;
|
||||||
|
length dir 8 ext filename dirname $256 fullpath $512 ;
|
||||||
|
call missing(of _all_);
|
||||||
|
fullpath = "&dir";
|
||||||
|
run;
|
||||||
|
|
||||||
|
%local sep;
|
||||||
|
%if &sysscp=WIN or &SYSSCP eq DNTHOST %then %let sep=\;
|
||||||
|
%else %let sep=/;
|
||||||
|
|
||||||
|
data &outds ;
|
||||||
|
modify &outds ;
|
||||||
|
retain sep "&sep";
|
||||||
|
rc=filename('tmp',fullpath);
|
||||||
|
dir_id=dopen('tmp');
|
||||||
|
dir = (dir_id ne 0) ;
|
||||||
|
if dir then dirname=fullpath;
|
||||||
|
else do;
|
||||||
|
filename=scan(fullpath,-1,sep) ;
|
||||||
|
dirname =substrn(fullpath,1,length(fullpath)-length(filename));
|
||||||
|
if index(filename,'.')>1 then ext=scan(filename,-1,'.');
|
||||||
|
end;
|
||||||
|
replace;
|
||||||
|
if dir then do;
|
||||||
|
do i=1 to dnum(dir_id);
|
||||||
|
fullpath=cats(dirname,sep,dread(dir_id,i));
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
rc=dclose(dir_id);
|
||||||
|
end;
|
||||||
|
rc=filename('tmp');
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
%mp_unzip(ziploc="/some/file.zip",outdir=/some/folder)
|
%mp_unzip(ziploc="/some/file.zip",outdir=/some/folder)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_mkdir.sas
|
@li mf_mkdir.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
@param var The variable to modify
|
@param var The variable to modify
|
||||||
@param len The new length to apply
|
@param len The new length to apply
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_existds.sas
|
@li mf_existds.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_existvar.sas
|
@li mf_existvar.sas
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
be sure that _debug is not set (else the SPWA will send non zipped content
|
be sure that _debug is not set (else the SPWA will send non zipped content
|
||||||
as well).
|
as well).
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_dirlist.sas
|
@li mp_dirlist.sas
|
||||||
|
|
||||||
@param in= unquoted filepath, dataset of files or directory to zip
|
@param in= unquoted filepath, dataset of files or directory to zip
|
||||||
|
|||||||
9
build.py
9
build.py
@@ -9,19 +9,22 @@ for file in files:
|
|||||||
ml = open('lua/' + name + '.sas', "w")
|
ml = open('lua/' + name + '.sas', "w")
|
||||||
ml.write("/**\n")
|
ml.write("/**\n")
|
||||||
ml.write(" @file " + name + '.sas\n')
|
ml.write(" @file " + name + '.sas\n')
|
||||||
ml.write(" @brief Creates the " + basename + " file\n")
|
ml.write(" @brief Compiles the " + basename + " lua file\n")
|
||||||
ml.write(" @details Writes " + basename + " to the work directory\n")
|
ml.write(" @details Writes " + basename + " to the work directory\n")
|
||||||
|
ml.write(" and then includes it.\n")
|
||||||
ml.write(" Usage:\n\n")
|
ml.write(" Usage:\n\n")
|
||||||
ml.write(" %" + name + "()\n\n")
|
ml.write(" %" + name + "()\n\n")
|
||||||
ml.write("**/\n\n")
|
ml.write("**/\n\n")
|
||||||
ml.write("%macro " + name + "();\n")
|
ml.write("%macro " + name + "();\n")
|
||||||
ml.write("data _null_;\n")
|
ml.write("data _null_;\n")
|
||||||
ml.write(" file \"%sysfunc(pathname(work))/" + basename + "\";\n")
|
ml.write(" file \"%sysfunc(pathname(work))/" + name + ".lua\";\n")
|
||||||
with open(file) as infile:
|
with open(file) as infile:
|
||||||
for line in infile:
|
for line in infile:
|
||||||
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
|
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
|
||||||
ml.write("run;\n")
|
ml.write("run;\n\n")
|
||||||
|
ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\";\n\n")
|
||||||
ml.write("%mend;\n")
|
ml.write("%mend;\n")
|
||||||
|
|
||||||
ml.close()
|
ml.close()
|
||||||
|
|
||||||
# prepare web files
|
# prepare web files
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ HTML_FOOTER = ./doxy/new_footer.html
|
|||||||
HTML_EXTRA_STYLESHEET = ./doxy/new_stylesheet.css
|
HTML_EXTRA_STYLESHEET = ./doxy/new_stylesheet.css
|
||||||
INHERIT_DOCS = NO
|
INHERIT_DOCS = NO
|
||||||
INLINE_INFO = NO
|
INLINE_INFO = NO
|
||||||
INPUT = base meta metax viya
|
INPUT = base meta metax viya lua
|
||||||
LAYOUT_FILE = ./doxy/DoxygenLayout.xml
|
LAYOUT_FILE = ./doxy/DoxygenLayout.xml
|
||||||
MAX_INITIALIZER_LINES = 0
|
MAX_INITIALIZER_LINES = 0
|
||||||
PROJECT_NAME = Macro Core
|
PROJECT_NAME = Macro Core
|
||||||
PROJECT_LOGO = doxy/Macro_core_website_1.png
|
PROJECT_LOGO = doxy/Macro_core_website_1.png
|
||||||
|
PROJECT_BRIEF = "Production Ready Macros for SAS Application Developers"
|
||||||
RECURSIVE = YES
|
RECURSIVE = YES
|
||||||
REPEAT_BRIEF = NO
|
REPEAT_BRIEF = NO
|
||||||
SHOW_NAMESPACES = NO
|
SHOW_NAMESPACES = NO
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<tab type="classmembers" visible="no" title="" intro=""/>
|
<tab type="classmembers" visible="no" title="" intro=""/>
|
||||||
</tab>
|
</tab>
|
||||||
|
|
||||||
<tab type="filelist" visible="yes" title="" intro="List of Files Used in the Macro Core Library"/>
|
<tab type="filelist" visible="yes" title="" intro="List of Files Used in the Macro-Core Library"/>
|
||||||
|
|
||||||
<tab type="examples" visible="yes" title="" intro=""/>
|
<tab type="examples" visible="yes" title="" intro=""/>
|
||||||
</navindex>
|
</navindex>
|
||||||
@@ -101,11 +101,11 @@
|
|||||||
<!-- Layout definition for a directory page -->
|
<!-- Layout definition for a directory page -->
|
||||||
<directory>
|
<directory>
|
||||||
<briefdescription visible="yes"/>
|
<briefdescription visible="yes"/>
|
||||||
|
<detaileddescription visible="yes" title=""/>
|
||||||
<directorygraph visible="yes"/>
|
<directorygraph visible="yes"/>
|
||||||
<memberdecl>
|
<memberdecl>
|
||||||
<dirs visible="yes"/>
|
<dirs visible="yes"/>
|
||||||
<files visible="yes"/>
|
<files visible="yes"/>
|
||||||
</memberdecl>
|
</memberdecl>
|
||||||
<detaileddescription title=""/>
|
|
||||||
</directory>
|
</directory>
|
||||||
</doxygenlayout>
|
</doxygenlayout>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ mkdir $BUILD_FOLDER
|
|||||||
cp -r base $BUILD_FOLDER
|
cp -r base $BUILD_FOLDER
|
||||||
cp -r meta $BUILD_FOLDER
|
cp -r meta $BUILD_FOLDER
|
||||||
cp -r metax $BUILD_FOLDER
|
cp -r metax $BUILD_FOLDER
|
||||||
|
cp -r lua $BUILD_FOLDER
|
||||||
cp -r viya $BUILD_FOLDER
|
cp -r viya $BUILD_FOLDER
|
||||||
cp -r doxy $BUILD_FOLDER
|
cp -r doxy $BUILD_FOLDER
|
||||||
cp main.dox $BUILD_FOLDER
|
cp main.dox $BUILD_FOLDER
|
||||||
@@ -30,11 +31,15 @@ doxygen Doxyfile
|
|||||||
# refresh github pages site
|
# refresh github pages site
|
||||||
git clone git@github.com:sasjs/core.github.io.git
|
git clone git@github.com:sasjs/core.github.io.git
|
||||||
cd core.github.io
|
cd core.github.io
|
||||||
git rm -r *
|
rm -r *
|
||||||
mv $BUILD_FOLDER/out/doxy/* .
|
mv $BUILD_FOLDER/out/doxy/* .
|
||||||
echo 'core.sasjs.io' > CNAME
|
echo 'core.sasjs.io' > CNAME
|
||||||
git add *
|
git add .
|
||||||
git commit -m "build.sh build on $(date +%F:%H:%M:%S)"
|
git commit -m "build.sh build on $(date +%F:%H:%M:%S)"
|
||||||
git push
|
git push
|
||||||
|
npx sitemap-generator-cli https://core.sasjs.io
|
||||||
|
git add .
|
||||||
|
git commit -m "adding sitemap"
|
||||||
|
git push
|
||||||
|
|
||||||
echo "check it out: https://sasjs.github.io/core.github.io/files.html"
|
echo "check it out: https://sasjs.github.io/core.github.io/files.html"
|
||||||
|
|||||||
BIN
doxy/favicon.ico
BIN
doxy/favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,22 +1,23 @@
|
|||||||
|
<!-- HTML footer for doxygen 1.8.17-->
|
||||||
|
<!-- start footer part -->
|
||||||
<!--BEGIN GENERATE_TREEVIEW-->
|
<!--BEGIN GENERATE_TREEVIEW-->
|
||||||
<li class="footer"><b>$generatedby</b>
|
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
|
||||||
<a href="http://www.doxygen.org/index.html">
|
<ul>
|
||||||
<img class="footer" src="doxygen.png" alt="doxygen"/></a>
|
$navpath
|
||||||
|
<li class="footer">$generatedby
|
||||||
|
<a href="https://www.doxygen.org/index.html">
|
||||||
|
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
|
||||||
<i> For more information visit the </i> <a href="https://github.com/sasjs/core">Macro Core library</a>.</li>
|
<i> For more information visit the </i> <a href="https://github.com/sasjs/core">Macro Core library</a>.</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!--END GENERATE_TREEVIEW-->
|
<!--END GENERATE_TREEVIEW-->
|
||||||
<!--BEGIN !GENERATE_TREEVIEW-->
|
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||||
<hr class="footer"/>
|
<hr class="footer"/><address class="footer"><small>
|
||||||
<table width="100%"><tbody><tr><td>
|
$generatedby  <a href="http://www.doxygen.org/index.html">
|
||||||
For more information visit the <a href="https://github.com/sasjs/core">Macro Core library</a>.
|
<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
|
||||||
</td><td><address class="footer"><small>
|
</a> $doxygenversion
|
||||||
©$year<br/>
|
</small></address>
|
||||||
$generatedby <a href="http://www.doxygen.org/index.html">
|
|
||||||
<!--<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/>-->doxygen
|
|
||||||
</a> $doxygenversion
|
|
||||||
</small></address></tr></tbody></table>
|
|
||||||
<!--END !GENERATE_TREEVIEW-->
|
<!--END !GENERATE_TREEVIEW-->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<!-- HTML header for doxygen 1.8.14-->
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
<!-- HTML header for doxygen 1.8.17-->
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="https://www.w3.org/1999/xhtml" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||||
@@ -16,51 +16,51 @@ $search
|
|||||||
$mathjax
|
$mathjax
|
||||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||||
<link REL="icon" HREF="https://sasjs.io/img/runningman.jpg">
|
<link REL="icon" HREF="https://sasjs.io/img/runningman.jpg">
|
||||||
|
|
||||||
$extrastylesheet
|
$extrastylesheet
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||||
|
|
||||||
<!--BEGIN TITLEAREA-->
|
<!--BEGIN TITLEAREA-->
|
||||||
<div id="titlearea" style='background-color:white' >
|
<div id="titlearea">
|
||||||
<table cellspacing="0" cellpadding="0">
|
<table cellspacing="0" cellpadding="0">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr style="height: 26px;">
|
<tr style="height: 56px;">
|
||||||
<!--BEGIN PROJECT_LOGO-->
|
<!--BEGIN PROJECT_LOGO-->
|
||||||
<td id="projectlogo"></td>
|
<td id="projectlogo">
|
||||||
|
<img alt="Logo" src="$relpath^$projectlogo"/></td>
|
||||||
<!--END PROJECT_LOGO-->
|
<!--END PROJECT_LOGO-->
|
||||||
<!--BEGIN PROJECT_NAME-->
|
<!--BEGIN PROJECT_NAME-->
|
||||||
<td>
|
<td id="projectalign" style="padding-left: 0.5em;">
|
||||||
<div id="projectname">
|
<div id="projectname">
|
||||||
<!--a href=".">
|
<!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
|
||||||
<img alt="Macro Core" src="https://macropeople.com/wp-content/uploads/2018/05/macropeople2014retina_V2.png" height=60/>
|
|
||||||
</a-->
|
|
||||||
<a href=".">
|
|
||||||
<img alt="Macro Core" src="./Macro_core_website_1.png" height=60/>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">
|
||||||
|
Production Ready Macros for SAS Application Developers</br>
|
||||||
|
<a href="https://github.com/sasjs/core">
|
||||||
|
https://github.com/sasjs/core
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<meta name="Description" content="$projectbrief">
|
||||||
|
<!--END PROJECT_BRIEF-->
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<!--END PROJECT_NAME-->
|
||||||
|
<!--BEGIN !PROJECT_NAME-->
|
||||||
|
<!--BEGIN PROJECT_BRIEF-->
|
||||||
|
<td style="padding-left: 0.5em;">
|
||||||
|
<div id="projectbrief">$projectbrief</div>
|
||||||
<table style="padding-left: 2em;" cellspacing="0" cellpadding="0">
|
<table style="padding-left: 2em;" cellspacing="0" cellpadding="0">
|
||||||
<tr><td> Production Ready Macros for SAS Application Developers</td></tr>
|
<tr><td> Production Ready Macros for SAS Application Developers</td></tr>
|
||||||
<tr><td><a href="https://github.com/sasjs/core">
|
<tr><td><a href="https://github.com/sasjs/core">
|
||||||
https://github.com/sasjs/core
|
https://github.com/sasjs/core
|
||||||
</a></td></tr>
|
</a></td></tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
<!--END PROJECT_NAME-->
|
|
||||||
<!--BEGIN !PROJECT_NAME-->
|
|
||||||
<!--BEGIN PROJECT_BRIEF-->
|
|
||||||
<!--END PROJECT_BRIEF-->
|
<!--END PROJECT_BRIEF-->
|
||||||
<!--END !PROJECT_NAME-->
|
<!--END !PROJECT_NAME-->
|
||||||
<div class="header">
|
|
||||||
<div class="headertitle">
|
|
||||||
|
|
||||||
<!--BEGIN DISABLE_INDEX-->
|
<!--BEGIN DISABLE_INDEX-->
|
||||||
<!--BEGIN SEARCHENGINE-->
|
<!--BEGIN SEARCHENGINE-->
|
||||||
|
|
||||||
<td>$searchbox</td>
|
<td>$searchbox</td>
|
||||||
<!--END SEARCHENGINE-->
|
<!--END SEARCHENGINE-->
|
||||||
<!--END DISABLE_INDEX-->
|
<!--END DISABLE_INDEX-->
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
--
|
--
|
||||||
-- json2sas.lua (modified from json.lua)
|
-- json.lua
|
||||||
--
|
--
|
||||||
-- Copyright (c) 2019 rxi
|
-- Copyright (c) 2019 rxi
|
||||||
--
|
--
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
-- SOFTWARE.
|
-- SOFTWARE.
|
||||||
--
|
--
|
||||||
|
|
||||||
local json2sas = { _version = "0.1.2" }
|
json = { _version = "0.1.2" }
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-- Encode
|
-- Encode
|
||||||
@@ -122,7 +122,7 @@ encode = function(val, stack)
|
|||||||
error("unexpected type '" .. t .. "'")
|
error("unexpected type '" .. t .. "'")
|
||||||
end
|
end
|
||||||
|
|
||||||
function json2sas.encode(val)
|
function json.encode(val)
|
||||||
return ( encode(val) )
|
return ( encode(val) )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -356,7 +356,7 @@ parse = function(str, idx)
|
|||||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||||
end
|
end
|
||||||
|
|
||||||
function json2sas.decode(str)
|
function json.decode(str)
|
||||||
if type(str) ~= "string" then
|
if type(str) ~= "string" then
|
||||||
error("expected argument of type string, got " .. type(str))
|
error("expected argument of type string, got " .. type(str))
|
||||||
end
|
end
|
||||||
@@ -368,88 +368,4 @@ function json2sas.decode(str)
|
|||||||
return res
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
-- convert macro variable array into one variable and decode
|
return json
|
||||||
function json2sas.go(macvar)
|
|
||||||
local x=1
|
|
||||||
local cnt=0
|
|
||||||
local mac=sas.symget(macvar..'0')
|
|
||||||
local newstr=''
|
|
||||||
if mac and mac ~= '' then
|
|
||||||
cnt=mac
|
|
||||||
for x=1,cnt,1 do
|
|
||||||
mac=sas.symget(macvar..x)
|
|
||||||
if mac and mac ~= '' then
|
|
||||||
newstr=newstr..mac
|
|
||||||
else
|
|
||||||
return print(macvar..x..' NOT FOUND!!')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return print(macvar..'0 NOT FOUND!!')
|
|
||||||
end
|
|
||||||
-- print('mac:'..mac..'cnt:'..cnt..'newstr'..newstr)
|
|
||||||
local oneVar=json2sas.decode(newstr)
|
|
||||||
local jsdata=oneVar["data"]
|
|
||||||
local meta={}
|
|
||||||
local attrs={}
|
|
||||||
for tablename, data in pairs(jsdata) do -- each table
|
|
||||||
print("Processing table: "..tablename)
|
|
||||||
attrs[tablename]={}
|
|
||||||
for k, v in ipairs(data) do -- each row
|
|
||||||
if(k==1) then -- column names
|
|
||||||
for a, b in pairs(v) do
|
|
||||||
attrs[tablename][a]={}
|
|
||||||
attrs[tablename][a]["name"]=b
|
|
||||||
end
|
|
||||||
elseif(k==2) then -- get types
|
|
||||||
for a, b in pairs(v) do
|
|
||||||
if type(b)=='number' then
|
|
||||||
attrs[tablename][a]["type"]="N"
|
|
||||||
attrs[tablename][a]["length"]=8
|
|
||||||
else
|
|
||||||
attrs[tablename][a]["type"]="C"
|
|
||||||
attrs[tablename][a]["length"]=string.len(b)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else --update lengths
|
|
||||||
for a, b in pairs(v) do
|
|
||||||
if (type(b)=='string' and string.len(b)>attrs[tablename][a]["length"])
|
|
||||||
then
|
|
||||||
attrs[tablename][a]["length"]=string.len(b)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print(json2sas.encode(attrs[tablename])) -- show results
|
|
||||||
|
|
||||||
-- Now create the SAS table
|
|
||||||
sas.new_table("work."..tablename,attrs[tablename])
|
|
||||||
local dsid=sas.open("work."..tablename, "u")
|
|
||||||
for k, v in ipairs(data) do
|
|
||||||
if k>1 then
|
|
||||||
sas.append(dsid)
|
|
||||||
for a, b in pairs(v) do
|
|
||||||
sas.put_value(dsid, attrs[tablename][a]["name"], b)
|
|
||||||
end
|
|
||||||
sas.update(dsid)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
sas.close(dsid)
|
|
||||||
end
|
|
||||||
return json2sas.decode(newstr)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function quote(str)
|
|
||||||
return sas.quote(str)
|
|
||||||
end
|
|
||||||
function sasvar(str)
|
|
||||||
print("processing: "..str)
|
|
||||||
print(sas.symexist(str))
|
|
||||||
if sas.symexist(str)==1 then
|
|
||||||
return quote(str)..':'..quote(sas.symget(str))..','
|
|
||||||
end
|
|
||||||
return ''
|
|
||||||
end
|
|
||||||
|
|
||||||
return json2sas
|
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
/**
|
/**
|
||||||
@file ml_json2sas.sas
|
@file ml_json.sas
|
||||||
@brief Creates the json2sas.lua file
|
@brief Compiles the json.lua lua file
|
||||||
@details Writes json2sas.lua to the work directory
|
@details Writes json.lua to the work directory
|
||||||
|
and then includes it.
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%ml_json2sas()
|
%ml_json()
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro ml_json2sas();
|
%macro ml_json();
|
||||||
data _null_;
|
data _null_;
|
||||||
file "%sysfunc(pathname(work))/json2sas.lua";
|
file "%sysfunc(pathname(work))/ml_json.lua";
|
||||||
put '-- ';
|
put '-- ';
|
||||||
put '-- json2sas.lua (modified from json.lua) ';
|
put '-- json.lua ';
|
||||||
put '-- ';
|
put '-- ';
|
||||||
put '-- Copyright (c) 2019 rxi ';
|
put '-- Copyright (c) 2019 rxi ';
|
||||||
put '-- ';
|
put '-- ';
|
||||||
@@ -35,7 +36,7 @@ data _null_;
|
|||||||
put '-- SOFTWARE. ';
|
put '-- SOFTWARE. ';
|
||||||
put '-- ';
|
put '-- ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put 'local json2sas = { _version = "0.1.2" } ';
|
put 'json = { _version = "0.1.2" } ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put '------------------------------------------------------------------------------- ';
|
put '------------------------------------------------------------------------------- ';
|
||||||
put '-- Encode ';
|
put '-- Encode ';
|
||||||
@@ -135,7 +136,7 @@ data _null_;
|
|||||||
put ' error("unexpected type ''" .. t .. "''") ';
|
put ' error("unexpected type ''" .. t .. "''") ';
|
||||||
put 'end ';
|
put 'end ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put 'function json2sas.encode(val) ';
|
put 'function json.encode(val) ';
|
||||||
put ' return ( encode(val) ) ';
|
put ' return ( encode(val) ) ';
|
||||||
put 'end ';
|
put 'end ';
|
||||||
put ' ';
|
put ' ';
|
||||||
@@ -369,7 +370,7 @@ data _null_;
|
|||||||
put ' decode_error(str, idx, "unexpected character ''" .. chr .. "''") ';
|
put ' decode_error(str, idx, "unexpected character ''" .. chr .. "''") ';
|
||||||
put 'end ';
|
put 'end ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put 'function json2sas.decode(str) ';
|
put 'function json.decode(str) ';
|
||||||
put ' if type(str) ~= "string" then ';
|
put ' if type(str) ~= "string" then ';
|
||||||
put ' error("expected argument of type string, got " .. type(str)) ';
|
put ' error("expected argument of type string, got " .. type(str)) ';
|
||||||
put ' end ';
|
put ' end ';
|
||||||
@@ -381,90 +382,9 @@ data _null_;
|
|||||||
put ' return res ';
|
put ' return res ';
|
||||||
put 'end ';
|
put 'end ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put '-- convert macro variable array into one variable and decode ';
|
put 'return json ';
|
||||||
put 'function json2sas.go(macvar) ';
|
|
||||||
put ' local x=1 ';
|
|
||||||
put ' local cnt=0 ';
|
|
||||||
put ' local mac=sas.symget(macvar..''0'') ';
|
|
||||||
put ' local newstr='''' ';
|
|
||||||
put ' if mac and mac ~= '''' then ';
|
|
||||||
put ' cnt=mac ';
|
|
||||||
put ' for x=1,cnt,1 do ';
|
|
||||||
put ' mac=sas.symget(macvar..x) ';
|
|
||||||
put ' if mac and mac ~= '''' then ';
|
|
||||||
put ' newstr=newstr..mac ';
|
|
||||||
put ' else ';
|
|
||||||
put ' return print(macvar..x..'' NOT FOUND!!'') ';
|
|
||||||
put ' end ';
|
|
||||||
put ' end ';
|
|
||||||
put ' else ';
|
|
||||||
put ' return print(macvar..''0 NOT FOUND!!'') ';
|
|
||||||
put ' end ';
|
|
||||||
put ' -- print(''mac:''..mac..''cnt:''..cnt..''newstr''..newstr) ';
|
|
||||||
put ' local oneVar=json2sas.decode(newstr) ';
|
|
||||||
put ' local jsdata=oneVar["data"] ';
|
|
||||||
put ' local meta={} ';
|
|
||||||
put ' local attrs={} ';
|
|
||||||
put ' for tablename, data in pairs(jsdata) do -- each table ';
|
|
||||||
put ' print("Processing table: "..tablename) ';
|
|
||||||
put ' attrs[tablename]={} ';
|
|
||||||
put ' for k, v in ipairs(data) do -- each row ';
|
|
||||||
put ' if(k==1) then -- column names ';
|
|
||||||
put ' for a, b in pairs(v) do ';
|
|
||||||
put ' attrs[tablename][a]={} ';
|
|
||||||
put ' attrs[tablename][a]["name"]=b ';
|
|
||||||
put ' end ';
|
|
||||||
put ' elseif(k==2) then -- get types ';
|
|
||||||
put ' for a, b in pairs(v) do ';
|
|
||||||
put ' if type(b)==''number'' then ';
|
|
||||||
put ' attrs[tablename][a]["type"]="N" ';
|
|
||||||
put ' attrs[tablename][a]["length"]=8 ';
|
|
||||||
put ' else ';
|
|
||||||
put ' attrs[tablename][a]["type"]="C" ';
|
|
||||||
put ' attrs[tablename][a]["length"]=string.len(b) ';
|
|
||||||
put ' end ';
|
|
||||||
put ' end ';
|
|
||||||
put ' else --update lengths ';
|
|
||||||
put ' for a, b in pairs(v) do ';
|
|
||||||
put ' if (type(b)==''string'' and string.len(b)>attrs[tablename][a]["length"]) ';
|
|
||||||
put ' then ';
|
|
||||||
put ' attrs[tablename][a]["length"]=string.len(b) ';
|
|
||||||
put ' end ';
|
|
||||||
put ' end ';
|
|
||||||
put ' end ';
|
|
||||||
put ' end ';
|
|
||||||
put ' print(json2sas.encode(attrs[tablename])) -- show results ';
|
|
||||||
put ' ';
|
|
||||||
put ' -- Now create the SAS table ';
|
|
||||||
put ' sas.new_table("work."..tablename,attrs[tablename]) ';
|
|
||||||
put ' local dsid=sas.open("work."..tablename, "u") ';
|
|
||||||
put ' for k, v in ipairs(data) do ';
|
|
||||||
put ' if k>1 then ';
|
|
||||||
put ' sas.append(dsid) ';
|
|
||||||
put ' for a, b in pairs(v) do ';
|
|
||||||
put ' sas.put_value(dsid, attrs[tablename][a]["name"], b) ';
|
|
||||||
put ' end ';
|
|
||||||
put ' sas.update(dsid) ';
|
|
||||||
put ' end ';
|
|
||||||
put ' end ';
|
|
||||||
put ' sas.close(dsid) ';
|
|
||||||
put ' end ';
|
|
||||||
put ' return json2sas.decode(newstr) ';
|
|
||||||
put 'end ';
|
|
||||||
put ' ';
|
|
||||||
put ' ';
|
|
||||||
put 'function quote(str) ';
|
|
||||||
put ' return sas.quote(str) ';
|
|
||||||
put 'end ';
|
|
||||||
put 'function sasvar(str) ';
|
|
||||||
put ' print("processing: "..str) ';
|
|
||||||
put ' print(sas.symexist(str)) ';
|
|
||||||
put ' if sas.symexist(str)==1 then ';
|
|
||||||
put ' return quote(str)..'':''..quote(sas.symget(str))..'','' ';
|
|
||||||
put ' end ';
|
|
||||||
put ' return '''' ';
|
|
||||||
put 'end ';
|
|
||||||
put ' ';
|
|
||||||
put 'return json2sas ';
|
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
%inc "%sysfunc(pathname(work))/ml_json.lua";
|
||||||
|
|
||||||
%mend;
|
%mend;
|
||||||
16
main.dox
16
main.dox
@@ -13,6 +13,11 @@
|
|||||||
* Not metadata aware
|
* Not metadata aware
|
||||||
* No X command
|
* No X command
|
||||||
* Prefixes: _mf_, _mp_
|
* Prefixes: _mf_, _mp_
|
||||||
|
|
||||||
|
Macros starting `mf_` are macro _functions_ and can be used in assignment
|
||||||
|
statements. Those starting `mp_` are macro _procedures_, which generate
|
||||||
|
SAS statements, and must therefore be applied accordingly.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \dir meta
|
/*! \dir meta
|
||||||
@@ -45,4 +50,15 @@
|
|||||||
* No X command
|
* No X command
|
||||||
* Prefixes: _mv_
|
* Prefixes: _mv_
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \dir lua
|
||||||
|
* \brief Lua macros
|
||||||
|
* \details These macros have the following attributes:
|
||||||
|
|
||||||
|
* OS independent
|
||||||
|
* Work as LUA functions (they are immediately executed/compiled)
|
||||||
|
* Auto-generated from the plain source `.lua` files in the same directory
|
||||||
|
* Prefixes: _ml_
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file mm_adduser2group.sas
|
||||||
@brief Adds a user to a group
|
@brief Adds a user to a group
|
||||||
@details Adds a user to a metadata group. The macro first checks whether the
|
@details Adds a user to a metadata group. The macro first checks whether the
|
||||||
user is in that group, and if not, the user is added.
|
user is in that group, and if not, the user is added.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mm_adduser2group(user=sasdemo
|
%mm_adduser2group(user=sasdemo
|
||||||
,group=someGroup)
|
,group=someGroup)
|
||||||
|
|
||||||
|
|
||||||
@param user= the user name (not displayname)
|
@param user= the user name (not displayname)
|
||||||
@param group= the group to which to add the user
|
@param group= the group to which to add the user
|
||||||
|
@param mdebug= set to 1 to show debug info in log
|
||||||
|
|
||||||
@warning the macro does not check inherited group memberships - it looks at
|
@warning the macro does not check inherited group memberships - it looks at
|
||||||
direct members only
|
direct members only
|
||||||
@@ -68,7 +69,7 @@ run;
|
|||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%if %length(&syscc) ge 4 %then %do;
|
%if &syscc ge 4 %then %do;
|
||||||
%put WARNING: SYSCC=&syscc, exiting &sysmacroname;
|
%put WARNING: SYSCC=&syscc, exiting &sysmacroname;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
disconnect from MyAlias;
|
disconnect from MyAlias;
|
||||||
quit;
|
quit;
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getengine.sas
|
@li mf_getengine.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
%mm_assignlib(SOMEREF)
|
%mm_assignlib(SOMEREF)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|
||||||
@param libref the libref (not name) of the metadata library
|
@param libref the libref (not name) of the metadata library
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
rc=metadata_getattr(liburi,"Name",LibName);
|
rc=metadata_getattr(liburi,"Name",LibName);
|
||||||
/* now try and assign it */
|
/* now try and assign it */
|
||||||
if libname("&libref",,'meta',cats('liburi="',liburi,'";')) ne 0 then do;
|
if libname("&libref",,'meta',cats('liburi="',liburi,'";')) ne 0 then do;
|
||||||
|
putlog "&libref could not be assigned";
|
||||||
call symputx('msg',sysmsg(),'l');
|
call symputx('msg',sysmsg(),'l');
|
||||||
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
|
if "&mabort"='HARD' then call symputx('mp_abort',1,'l');
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
@warning application components do not get deleted when removing the container folder! be sure you have the administrative priviliges to remove this kind of metadata from the SMC plugin (or be ready to do to so programmatically).
|
@warning application components do not get deleted when removing the container folder! be sure you have the administrative priviliges to remove this kind of metadata from the SMC plugin (or be ready to do to so programmatically).
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_verifymacvars.sas
|
@li mf_verifymacvars.sas
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
%mm_createdataset(tableuri=G5X8AFW1.BE00015Y)
|
%mm_createdataset(tableuri=G5X8AFW1.BE00015Y)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mm_getlibs.sas
|
@li mm_getlibs.sas
|
||||||
@li mm_gettables.sas
|
@li mm_gettables.sas
|
||||||
@li mm_getcols.sas
|
@li mm_getcols.sas
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
%mm_createdocument(tree=/User Folders/sasdemo
|
%mm_createdocument(tree=/User Folders/sasdemo
|
||||||
,name=MyNote)
|
,name=MyNote)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_verifymacvars.sas
|
@li mf_verifymacvars.sas
|
||||||
|
|
||||||
|
|||||||
@@ -9,17 +9,17 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mm_createlibrary(
|
%mm_createlibrary(
|
||||||
libname=My New Library
|
libname=My New Library
|
||||||
,libref=mynewlib
|
,libref=mynewlib
|
||||||
,libdesc=Super & <fine>
|
,libdesc=Super & <fine>
|
||||||
,engine=BASE
|
,engine=BASE
|
||||||
,tree=/User Folders/sasdemo
|
,tree=/User Folders/sasdemo
|
||||||
,servercontext=SASApp
|
,servercontext=SASApp
|
||||||
,directory=/tmp/tests
|
,directory=/tmp/tests
|
||||||
,mDebug=1)
|
,mDebug=1)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_verifymacvars.sas
|
@li mf_verifymacvars.sas
|
||||||
@li mm_createfolder.sas
|
@li mm_createfolder.sas
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
,Server=SASApp
|
,Server=SASApp
|
||||||
,stptype=2)
|
,stptype=2)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@li mf_verifymacvars.sas
|
@li mf_verifymacvars.sas
|
||||||
@li mm_getdirectories.sas
|
@li mm_getdirectories.sas
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Usage:
|
|||||||
;;;;
|
;;;;
|
||||||
%mm_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
|
%mm_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001,replace=YES)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mm_createstp.sas
|
@li mm_createstp.sas
|
||||||
@li mf_getuser.sas
|
@li mf_getuser.sas
|
||||||
@li mm_createfolder.sas
|
@li mm_createfolder.sas
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
%mm_createdocument(tree=/User Folders/&sysuserid,name=MyNote)
|
%mm_createdocument(tree=/User Folders/&sysuserid,name=MyNote)
|
||||||
%mm_deletedocument(target=/User Folders/&sysuserid/MyNote)
|
%mm_deletedocument(target=/User Folders/&sysuserid/MyNote)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
|
||||||
@param target= full path to the document being deleted
|
@param target= full path to the document being deleted
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
%mm_deletestp(target=/some/meta/path/myStoredProcess)
|
%mm_deletestp(target=/some/meta/path/myStoredProcess)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
|
||||||
@param target= full path to the STP being deleted
|
@param target= full path to the STP being deleted
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
@param outds= the ONE LEVEL work dataset to create
|
@param outds= the ONE LEVEL work dataset to create
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mm_getobjects.sas
|
@li mm_getobjects.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@li mm_getdetails.sas
|
@li mm_getdetails.sas
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
,outref=/some/unquoted/filename.ext
|
,outref=/some/unquoted/filename.ext
|
||||||
)
|
)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|
||||||
@param tree= The metadata path of the document
|
@param tree= The metadata path of the document
|
||||||
@@ -124,6 +124,7 @@ data _null_;
|
|||||||
when ('
') rec='0A'x;
|
when ('
') rec='0A'x;
|
||||||
when ('
') rec='0D'x;
|
when ('
') rec='0D'x;
|
||||||
when ('$' ) rec='$' ;
|
when ('$' ) rec='$' ;
|
||||||
|
when ('	') rec='09'x;
|
||||||
otherwise putlog "WARNING: missing value for " entity=;
|
otherwise putlog "WARNING: missing value for " entity=;
|
||||||
end;
|
end;
|
||||||
rc =fput(fileid, substr(rec,1,1));
|
rc =fput(fileid, substr(rec,1,1));
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
@param outds= the dataset to create that contains the list of directories
|
@param outds= the dataset to create that contains the list of directories
|
||||||
@param mDebug= set to 1 to show debug messages in the log
|
@param mDebug= set to 1 to show debug messages in the log
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
@@ -5,17 +5,15 @@
|
|||||||
blank to return all groups.
|
blank to return all groups.
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
- all groups
|
- all groups: `%mm_getGroups()`
|
||||||
%mm_getGroups()
|
|
||||||
|
|
||||||
- all groups for a particular user
|
- all groups for a particular user: `%mm_getgroups(user=&sysuserid)`
|
||||||
%mm_getgroups(user=&sysuserid)
|
|
||||||
|
|
||||||
@param user= the metadata user to return groups for. Leave blank for all
|
@param [in] user= the metadata user to return groups for. Leave blank for all
|
||||||
groups.
|
groups.
|
||||||
@param outds= the dataset to create that contains the list of groups
|
@param [in] repo= the metadata repository that contains the user/group information
|
||||||
@param repo= the metadata repository that contains the user/group information
|
@param [in] mDebug= set to 1 to show debug messages in the log
|
||||||
@param mDebug= set to 1 to show debug messages in the log
|
@param [out] outds= the dataset to create that contains the list of groups
|
||||||
|
|
||||||
@returns outds dataset containing all groups in a column named "metagroup"
|
@returns outds dataset containing all groups in a column named "metagroup"
|
||||||
- groupuri
|
- groupuri
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mm_getroles()
|
%mm_getroles()
|
||||||
|
|
||||||
@param outds the dataset to create that contains the list of roles
|
@param [out] outds the dataset to create that contains the list of roles
|
||||||
|
|
||||||
@returns outds dataset containing all roles, with the following columns:
|
@returns outds dataset containing all roles, with the following columns:
|
||||||
- uri
|
- uri
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
filename __mc2 clear;
|
filename __mc2 clear;
|
||||||
libname __mc3 clear;
|
libname __mc3 clear;
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mm_getrepos.sas
|
@li mm_getrepos.sas
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ data _null_;
|
|||||||
when ('
') rec='0A'x;
|
when ('
') rec='0A'x;
|
||||||
when ('
') rec='0D'x;
|
when ('
') rec='0D'x;
|
||||||
when ('$' ) rec='$' ;
|
when ('$' ) rec='$' ;
|
||||||
|
when ('	') rec='09'x;
|
||||||
otherwise putlog "%str(WARN)ING: missing value for " entity=;
|
otherwise putlog "%str(WARN)ING: missing value for " entity=;
|
||||||
end;
|
end;
|
||||||
rc =fput(fileid, substr(rec,1,1));
|
rc =fput(fileid, substr(rec,1,1));
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
%mm_getstps(tree=/My Folder/My STPs, name=My STP)
|
%mm_getstps(tree=/My Folder/My STPs, name=My STP)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mm_gettree.sas
|
@li mm_gettree.sas
|
||||||
|
|
||||||
@param tree= the metadata folder location in which to search. Leave blank
|
@param tree= the metadata folder location in which to search. Leave blank
|
||||||
@@ -23,8 +23,8 @@
|
|||||||
combine with the <code>tree=</code> parameter.
|
combine with the <code>tree=</code> parameter.
|
||||||
@param outds= the dataset to create that contains the list of stps.
|
@param outds= the dataset to create that contains the list of stps.
|
||||||
@param mDebug= set to 1 to show debug messages in the log
|
@param mDebug= set to 1 to show debug messages in the log
|
||||||
@showDesc= provide a non blank value to return stored process descriptions
|
@param showDesc= provide a non blank value to return stored process descriptions
|
||||||
@showUsageVersion= provide a non blank value to return the UsageVersion. This
|
@param showUsageVersion= provide a non blank value to return the UsageVersion. This
|
||||||
is either 1000000 (type 1, 9.2) or 2000000 (type2, 9.3 onwards).
|
is either 1000000 (type 1, 9.2) or 2000000 (type2, 9.3 onwards).
|
||||||
|
|
||||||
@returns outds dataset containing the following columns
|
@returns outds dataset containing the following columns
|
||||||
|
|||||||
67
meta/mm_gettableid.sas
Normal file
67
meta/mm_gettableid.sas
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
@file mm_gettableid.sas
|
||||||
|
@brief Get the metadata id for a particular table
|
||||||
|
@details Provide a libref and table name to return the corresponding metadata id
|
||||||
|
in an output datataset.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
- get a table id
|
||||||
|
%mm_gettableid(libref=METALIB,ds=SOMETABLE,outds=iwant)
|
||||||
|
|
||||||
|
@param libref= The libref to search
|
||||||
|
@param ds= The input dataset to check
|
||||||
|
@param outds= the dataset to create that contains the `tableuri`
|
||||||
|
@param mDebug= set to 1 to show debug messages in the log
|
||||||
|
|
||||||
|
@returns outds dataset containing `tableuri` and `tablename`
|
||||||
|
|
||||||
|
@version 9.3
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mm_gettableid(
|
||||||
|
libref=
|
||||||
|
,ds=
|
||||||
|
,outds=work.mm_gettableid
|
||||||
|
,mDebug=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%local mD;
|
||||||
|
%if &mDebug=1 %then %let mD=;
|
||||||
|
%else %let mD=%str(*);
|
||||||
|
%&mD.put Executing &sysmacroname..sas;
|
||||||
|
%&mD.put _local_;
|
||||||
|
|
||||||
|
data &outds;
|
||||||
|
length uri usingpkguri id type tableuri tablename tmpuri $256;
|
||||||
|
call missing(of _all_);
|
||||||
|
keep tableuri tablename;
|
||||||
|
n=1;
|
||||||
|
rc=0;
|
||||||
|
if metadata_getnobj("omsobj:SASLibrary?@Libref='&libref'",n,uri)<1 then do;
|
||||||
|
put "Library &libref not found";
|
||||||
|
stop;
|
||||||
|
end;
|
||||||
|
&mD.putlog "uri is " uri;
|
||||||
|
if metadata_getnasn(uri, "UsingPackages", 1, usingpkguri)>0 then do;
|
||||||
|
rc=metadata_resolve(usingpkguri,type,id);
|
||||||
|
&mD.putlog "Type is " type;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if type='DatabaseSchema' then tmpuri=usingpkguri;
|
||||||
|
else tmpuri=uri;
|
||||||
|
|
||||||
|
t=1;
|
||||||
|
do while(metadata_getnasn(tmpuri, "Tables", t, tableuri)>0);
|
||||||
|
t+1;
|
||||||
|
rc= metadata_getattr(tableuri, "Name", tablename);
|
||||||
|
&mD.putlog "Table is " tablename;
|
||||||
|
if upcase(tablename)="%upcase(&ds)" then do;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Retrieves properties of the SAS web app server
|
@brief Retrieves properties of the SAS web app server
|
||||||
@details
|
@details
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mm_getwebappsrvprops(outds= some_ds)
|
%mm_getwebappsrvprops(outds= some_ds)
|
||||||
@@ -21,8 +21,7 @@
|
|||||||
libname __shake clear;
|
libname __shake clear;
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -99,6 +98,7 @@ run;
|
|||||||
when ('
') rec='0A'x;
|
when ('
') rec='0A'x;
|
||||||
when ('
') rec='0D'x;
|
when ('
') rec='0D'x;
|
||||||
when ('$' ) rec='$' ;
|
when ('$' ) rec='$' ;
|
||||||
|
when ('	') rec='09'x;
|
||||||
otherwise putlog "WARNING: missing value for " entity=;
|
otherwise putlog "WARNING: missing value for " entity=;
|
||||||
end;
|
end;
|
||||||
rc =fput(fileid, substr(rec,1,1));
|
rc =fput(fileid, substr(rec,1,1));
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
./mmscript.sh "myuser" "mypass"
|
./mmscript.sh "myuser" "mypass"
|
||||||
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_loc.sas
|
@li mf_loc.sas
|
||||||
@li mm_tree.sas
|
@li mm_tree.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
Table
|
Table
|
||||||
,outds=morestuff)
|
,outds=morestuff)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getquotedstr.sas
|
@li mf_getquotedstr.sas
|
||||||
@li mm_getpublictypes.sas
|
@li mm_getpublictypes.sas
|
||||||
@li mf_isblank.sas
|
@li mf_isblank.sas
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
%mm_updatestpservertype(target=/some/meta/path/myStoredProcess
|
%mm_updatestpservertype(target=/some/meta/path/myStoredProcess
|
||||||
,type=WKS)
|
,type=WKS)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
|
|
||||||
@param target= full path to the STP being deleted
|
@param target= full path to the STP being deleted
|
||||||
@param type= Either WKS or STP depending on whether Workspace or Stored Process
|
@param type= Either WKS or STP depending on whether Workspace or Stored Process
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
%mmx_deletemetafolder(loc=/some/meta/folder,user=sasdemo,pass=mars345)
|
%mmx_deletemetafolder(loc=/some/meta/folder,user=sasdemo,pass=mars345)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_loc.sas
|
@li mf_loc.sas
|
||||||
|
|
||||||
@param loc= the metadata folder to delete
|
@param loc= the metadata folder to delete
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Usage:
|
|||||||
,outspkpath=%str(/tmp)
|
,outspkpath=%str(/tmp)
|
||||||
)
|
)
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_loc.sas
|
@li mf_loc.sas
|
||||||
@li mm_tree.sas
|
@li mm_tree.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
|
|||||||
@@ -15,10 +15,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@li mf_getuniquelibref.sas
|
@li mf_getuniquelibref.sas
|
||||||
@@ -42,7 +41,7 @@
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
and &grant_type ne sas_services
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -96,7 +95,7 @@ options noquotelenmax;
|
|||||||
data _null_;
|
data _null_;
|
||||||
set &libref1..links;
|
set &libref1..links;
|
||||||
if rel='createChild' then
|
if rel='createChild' then
|
||||||
call symputx('href',quote(trim(href)),'l');
|
call symputx('href',quote("&base_uri"!!trim(href)),'l');
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
%else %if &SYS_PROCHTTP_STATUS_CODE=404 %then %do;
|
%else %if &SYS_PROCHTTP_STATUS_CODE=404 %then %do;
|
||||||
@@ -121,7 +120,7 @@ options noquotelenmax;
|
|||||||
out=&fname2
|
out=&fname2
|
||||||
&oauth_bearer
|
&oauth_bearer
|
||||||
url=%unquote(%superq(href));
|
url=%unquote(%superq(href));
|
||||||
headers
|
headers
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
"Authorization"="Bearer &&&access_token_var"
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -1,34 +1,35 @@
|
|||||||
/**
|
/**
|
||||||
@file mv_createwebservice.sas
|
@file mv_createwebservice.sas
|
||||||
@brief Creates a JobExecution web service if it doesn't already exist
|
@brief Creates a JobExecution web service if it doesn't already exist
|
||||||
@details Code is passed in as one or more filerefs.
|
@details
|
||||||
|
Code is passed in as one or more filerefs.
|
||||||
|
|
||||||
%* Step 1 - compile macros ;
|
%* Step 1 - compile macros ;
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
%* Step 2 - Create some code and add it to a web service;
|
%* Step 2 - Create some code and add it to a web service;
|
||||||
filename ft15f001 temp;
|
filename ft15f001 temp;
|
||||||
parmcards4;
|
parmcards4;
|
||||||
%webout(FETCH) %* fetch any tables sent from frontend;
|
%webout(FETCH) %* fetch any tables sent from frontend;
|
||||||
%* do some sas, any inputs are now already WORK tables;
|
%* do some sas, any inputs are now already WORK tables;
|
||||||
data example1 example2;
|
data example1 example2;
|
||||||
set sashelp.class;
|
set sashelp.class;
|
||||||
run;
|
run;
|
||||||
%* send data back;
|
%* send data back;
|
||||||
%webout(OPEN)
|
%webout(OPEN)
|
||||||
%webout(ARR,example1) * Array format, fast, suitable for large tables ;
|
%webout(ARR,example1) * Array format, fast, suitable for large tables ;
|
||||||
%webout(OBJ,example2) * Object format, easier to work with ;
|
%webout(OBJ,example2) * Object format, easier to work with ;
|
||||||
%webout(CLOSE)
|
%webout(CLOSE)
|
||||||
;;;;
|
;;;;
|
||||||
%mv_createwebservice(path=/Public/app/common,name=appinit)
|
%mv_createwebservice(path=/Public/app/common,name=appinit)
|
||||||
|
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
To minimise postgres requests, output json is stored in a temporary file
|
To minimise postgres requests, output json is stored in a temporary file
|
||||||
and then sent to _webout in one go at the end.
|
and then sent to _webout in one go at the end.
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mv_createfolder.sas
|
@li mv_createfolder.sas
|
||||||
@li mf_getuniquelibref.sas
|
@li mf_getuniquelibref.sas
|
||||||
@@ -54,8 +55,7 @@
|
|||||||
a shared context - see https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
a shared context - see https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ libname &libref1 JSON fileref=&fname1;
|
|||||||
|
|
||||||
data _null_;
|
data _null_;
|
||||||
set &libref1..links;
|
set &libref1..links;
|
||||||
if rel='members' then call symputx('membercheck',quote(trim(href)),'l');
|
if rel='members' then call symputx('membercheck',quote("&base_uri"!!trim(href)),'l');
|
||||||
else if rel='self' then call symputx('parentFolderUri',href,'l');
|
else if rel='self' then call symputx('parentFolderUri',href,'l');
|
||||||
run;
|
run;
|
||||||
data _null_;
|
data _null_;
|
||||||
@@ -338,7 +338,7 @@ data _null_;
|
|||||||
put '%mend; ';
|
put '%mend; ';
|
||||||
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y); ';
|
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y); ';
|
||||||
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
|
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
|
||||||
put ' sasjs_tables; ';
|
put ' sasjs_tables SYS_JES_JOB_URI; ';
|
||||||
put '%if %index("&_debug",log) %then %let _debug=131; ';
|
put '%if %index("&_debug",log) %then %let _debug=131; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put '%local i tempds; ';
|
put '%local i tempds; ';
|
||||||
@@ -441,8 +441,13 @@ data _null_;
|
|||||||
put '%else %if &action=OPEN %then %do; ';
|
put '%else %if &action=OPEN %then %do; ';
|
||||||
put ' /* setup webout */ ';
|
put ' /* setup webout */ ';
|
||||||
put ' OPTIONS NOBOMFILE; ';
|
put ' OPTIONS NOBOMFILE; ';
|
||||||
put ' filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" ';
|
put ' %if "X&SYS_JES_JOB_URI.X"="XX" %then %do; ';
|
||||||
put ' name="_webout.json" lrecl=999999 mod; ';
|
put ' filename _webout temp lrecl=999999 mod; ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %do; ';
|
||||||
|
put ' filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" ';
|
||||||
|
put ' name="_webout.json" lrecl=999999 mod; ';
|
||||||
|
put ' %end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put ' /* setup temp ref */ ';
|
put ' /* setup temp ref */ ';
|
||||||
put ' %if %upcase(&fref) ne _WEBOUT %then %do; ';
|
put ' %if %upcase(&fref) ne _WEBOUT %then %do; ';
|
||||||
@@ -472,13 +477,13 @@ data _null_;
|
|||||||
put ' i+1; ';
|
put ' i+1; ';
|
||||||
put ' call symputx(''wt''!!left(i),name); ';
|
put ' call symputx(''wt''!!left(i),name); ';
|
||||||
put ' call symputx(''wtcnt'',i); ';
|
put ' call symputx(''wtcnt'',i); ';
|
||||||
put ' data _null_; file &fref; put ",""WORK"":{"; ';
|
put ' data _null_; file &fref mod; put ",""WORK"":{"; ';
|
||||||
put ' %do i=1 %to &wtcnt; ';
|
put ' %do i=1 %to &wtcnt; ';
|
||||||
put ' %let wt=&&wt&i; ';
|
put ' %let wt=&&wt&i; ';
|
||||||
put ' proc contents noprint data=&wt ';
|
put ' proc contents noprint data=&wt ';
|
||||||
put ' out=_data_ (keep=name type length format:); ';
|
put ' out=_data_ (keep=name type length format:); ';
|
||||||
put ' run;%let tempds=%scan(&syslast,2,.); ';
|
put ' run;%let tempds=%scan(&syslast,2,.); ';
|
||||||
put ' data _null_; file &fref; ';
|
put ' data _null_; file &fref mod; ';
|
||||||
put ' dsid=open("WORK.&wt",''is''); ';
|
put ' dsid=open("WORK.&wt",''is''); ';
|
||||||
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
put ' nlobs=attrn(dsid,''NLOBS''); ';
|
||||||
put ' nvars=attrn(dsid,''NVARS''); ';
|
put ' nvars=attrn(dsid,''NVARS''); ';
|
||||||
@@ -489,9 +494,9 @@ data _null_;
|
|||||||
put ' put '',"nvars":'' nvars; ';
|
put ' put '',"nvars":'' nvars; ';
|
||||||
put ' %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) ';
|
put ' %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) ';
|
||||||
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) ';
|
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) ';
|
||||||
put ' data _null_; file &fref;put "}"; ';
|
put ' data _null_; file &fref mod;put "}"; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' data _null_; file &fref;put "}";run; ';
|
put ' data _null_; file &fref mod;put "}";run; ';
|
||||||
put ' %end; ';
|
put ' %end; ';
|
||||||
put ' ';
|
put ' ';
|
||||||
put ' /* close off json */ ';
|
put ' /* close off json */ ';
|
||||||
@@ -604,7 +609,7 @@ proc http method='POST'
|
|||||||
in=&fname3
|
in=&fname3
|
||||||
out=&fname4
|
out=&fname4
|
||||||
&oauth_bearer
|
&oauth_bearer
|
||||||
url="/jobDefinitions/definitions?parentFolderUri=&parentFolderUri";
|
url="&base_uri/jobDefinitions/definitions?parentFolderUri=&parentFolderUri";
|
||||||
headers 'Content-Type'='application/vnd.sas.job.definition+json'
|
headers 'Content-Type'='application/vnd.sas.job.definition+json'
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
"Authorization"="Bearer &&&access_token_var"
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
@file mv_deletefoldermember.sas
|
@file mv_deletefoldermember.sas
|
||||||
@brief Deletes an item in a Viya folder
|
@brief Deletes an item in a Viya folder
|
||||||
@details If not executed in Studio 5+ will expect oauth token in a global
|
@details If not executed in Studio 5+ will expect oauth token in a global
|
||||||
macro variable (default ACCESS_TOKEN).
|
macro variable (default ACCESS_TOKEN).
|
||||||
|
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
@@ -20,10 +20,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@@ -48,7 +47,7 @@
|
|||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
and &grant_type ne sas_services
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -98,7 +97,7 @@ run;
|
|||||||
libname &libref1 JSON fileref=&fname1;
|
libname &libref1 JSON fileref=&fname1;
|
||||||
data _null_;
|
data _null_;
|
||||||
set &libref1..links;
|
set &libref1..links;
|
||||||
if rel='members' then call symputx('mref',quote(trim(href)),'l');
|
if rel='members' then call symputx('mref',quote("&base_uri"!!trim(href)),'l');
|
||||||
run;
|
run;
|
||||||
|
|
||||||
/* get the children */
|
/* get the children */
|
||||||
@@ -128,8 +127,8 @@ run;
|
|||||||
%put NOTE:;%put NOTE- &sysmacroname: &path/&name NOT FOUND;%put NOTE- ;
|
%put NOTE:;%put NOTE- &sysmacroname: &path/&name NOT FOUND;%put NOTE- ;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
proc http method="DELETE" url="&uri" &oauth_bearer;
|
proc http method="DELETE" url="&base_uri&uri" &oauth_bearer;
|
||||||
headers
|
headers
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
"Authorization"="Bearer &&&access_token_var"
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
@file mv_deletejes.sas
|
@file
|
||||||
@brief Creates a job execution service if it does not already exist
|
@brief Deletes a Viya Job, if it exists
|
||||||
@details If not executed in Studio 5+ will expect oauth token in a global
|
@details If not executed in Studio 5+ will expect oauth token in a global
|
||||||
macro variable (default ACCESS_TOKEN).
|
macro variable (default ACCESS_TOKEN).
|
||||||
|
|
||||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
@@ -19,10 +19,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@@ -46,7 +45,7 @@
|
|||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
and &grant_type ne sas_services
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -95,7 +94,7 @@ run;
|
|||||||
libname &libref1 JSON fileref=&fname1;
|
libname &libref1 JSON fileref=&fname1;
|
||||||
data _null_;
|
data _null_;
|
||||||
set &libref1..links;
|
set &libref1..links;
|
||||||
if rel='members' then call symputx('mref',quote(trim(href)),'l');
|
if rel='members' then call symputx('mref',quote("&base_uri"!!trim(href)),'l');
|
||||||
run;
|
run;
|
||||||
|
|
||||||
/* get the children */
|
/* get the children */
|
||||||
@@ -117,7 +116,7 @@ libname &libref1a JSON fileref=&fname1a;
|
|||||||
data _null_;
|
data _null_;
|
||||||
set &libref1a..items;
|
set &libref1a..items;
|
||||||
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
|
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
|
||||||
call symputx('uri',uri,'l');
|
call symputx('uri',cats("&base_uri",uri),'l');
|
||||||
call symputx('found',1,'l');
|
call symputx('found',1,'l');
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
@@ -126,7 +125,7 @@ run;
|
|||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
proc http method="DELETE" url="&uri" &oauth_bearer;
|
proc http method="DELETE" url="&uri" &oauth_bearer;
|
||||||
headers
|
headers
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
"Authorization"="Bearer &&&access_token_var"
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
@file mv_deleteviyafolder.sas
|
@file mv_deleteviyafolder.sas
|
||||||
@brief Creates a viya folder if that folder does not already exist
|
@brief Creates a viya folder if that folder does not already exist
|
||||||
@details If not running in Studo 5 +, will expect an oauth token in a global
|
@details If not running in Studo 5 +, will expect an oauth token in a global
|
||||||
macro variable (default ACCESS_TOKEN).
|
macro variable (default ACCESS_TOKEN).
|
||||||
|
|
||||||
options mprint;
|
options mprint;
|
||||||
@@ -16,10 +16,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@@ -42,7 +41,7 @@
|
|||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
and &grant_type ne sas_services
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -88,9 +87,9 @@ libname &libref1 JSON fileref=&fname1;
|
|||||||
data _null_;
|
data _null_;
|
||||||
set &libref1..links;
|
set &libref1..links;
|
||||||
if rel='deleteRecursively' then
|
if rel='deleteRecursively' then
|
||||||
call symputx('href',quote(trim(href)),'l');
|
call symputx('href',quote("&base_uri"!!trim(href)),'l');
|
||||||
else if rel='members' then
|
else if rel='members' then
|
||||||
call symputx('mref',quote(cats(href,'?recursive=true')),'l');
|
call symputx('mref',quote(cats("&base_uri",href,'?recursive=true')),'l');
|
||||||
run;
|
run;
|
||||||
|
|
||||||
/* before we can delete the folder, we need to delete the children */
|
/* before we can delete the folder, we need to delete the children */
|
||||||
@@ -111,7 +110,7 @@ data _null_;
|
|||||||
set &libref1a..items_links;
|
set &libref1a..items_links;
|
||||||
if href=:'/folders/folders' then return;
|
if href=:'/folders/folders' then return;
|
||||||
if rel='deleteResource' then
|
if rel='deleteResource' then
|
||||||
call execute('proc http method="DELETE" url='!!quote(trim(href))
|
call execute('proc http method="DELETE" url='!!quote("&base_uri"!!trim(href))
|
||||||
!!'; headers "Authorization"="Bearer &&&access_token_var" '
|
!!'; headers "Authorization"="Bearer &&&access_token_var" '
|
||||||
!!' "Accept"="*/*";run; /**/');
|
!!' "Accept"="*/*";run; /**/');
|
||||||
run;
|
run;
|
||||||
@@ -121,10 +120,10 @@ run;
|
|||||||
%let fname2=%mf_getuniquefileref();
|
%let fname2=%mf_getuniquefileref();
|
||||||
proc http method='DELETE' out=&fname2 &oauth_bearer
|
proc http method='DELETE' out=&fname2 &oauth_bearer
|
||||||
url=%unquote(%superq(href));
|
url=%unquote(%superq(href));
|
||||||
headers
|
headers
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
"Authorization"="Bearer &&&access_token_var"
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
%end;
|
%end;
|
||||||
'Accept'='*/*'; /**/
|
'Accept'='*/*'; /**/
|
||||||
run;
|
run;
|
||||||
%if &SYS_PROCHTTP_STATUS_CODE ne 204 %then %do;
|
%if &SYS_PROCHTTP_STATUS_CODE ne 204 %then %do;
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
@brief deprecated - replaced by mv_tokenrefresh.sas
|
@brief deprecated - replaced by mv_tokenrefresh.sas
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
<h4> Dependencies </h4>
|
|
||||||
@li mv_tokenrefresh.sas
|
@li mv_tokenrefresh.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mv_getaccesstoken(client_id=someclient
|
%macro mv_getaccesstoken(client_id=someclient
|
||||||
,client_secret=somesecret
|
,client_secret=somesecret
|
||||||
,grant_type=authorization_code
|
,grant_type=authorization_code
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
@brief deprecated - replaced by mv_registerclient.sas
|
@brief deprecated - replaced by mv_registerclient.sas
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
<h4> Dependencies </h4>
|
|
||||||
@li mv_registerclient.sas
|
@li mv_registerclient.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mv_getapptoken(client_id=someclient
|
%macro mv_getapptoken(client_id=someclient
|
||||||
,client_secret=somesecret
|
,client_secret=somesecret
|
||||||
,grant_type=authorization_code
|
,grant_type=authorization_code
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
@file mv_getgroups.sas
|
@file mv_getclients.sas
|
||||||
@brief Creates a dataset with a list of viya groups
|
@brief Get a list of Viya Clients
|
||||||
@details First, be sure you have an access token (which requires an app token).
|
@details First, be sure you have an access token (which requires an app token).
|
||||||
|
|
||||||
Using the macros here:
|
Using the macros here:
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
Now we can run the macro!
|
Now we can run the macro!
|
||||||
|
|
||||||
%mv_getgroups()
|
%mv_getclients()
|
||||||
|
|
||||||
@param access_token_var= The global macro variable to contain the access token
|
@param access_token_var= The global macro variable to contain the access token
|
||||||
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
@param grant_type= valid values are "password" or "authorization_code" (unquoted).
|
||||||
@@ -29,10 +29,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@@ -95,7 +94,7 @@ run;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* clear refs
|
/* clear refs
|
||||||
filename &fname1 clear;
|
filename &fname1 clear;
|
||||||
libname &libref1 clear;
|
libname &libref1 clear;
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,10 +15,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@@ -42,7 +41,7 @@
|
|||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
and &grant_type ne sas_services
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -64,7 +63,7 @@ options noquotelenmax;
|
|||||||
%if "&root"="/" %then %do;
|
%if "&root"="/" %then %do;
|
||||||
/* if root just list root folders */
|
/* if root just list root folders */
|
||||||
proc http method='GET' out=&fname1 &oauth_bearer
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
url='%sysfunc(getoption(servicesbaseurl))/folders/rootFolders';
|
url="&base_uri/folders/rootFolders";
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
headers "Authorization"="Bearer &&&access_token_var";
|
headers "Authorization"="Bearer &&&access_token_var";
|
||||||
%end;
|
%end;
|
||||||
@@ -85,10 +84,16 @@ options noquotelenmax;
|
|||||||
/*data _null_;infile &fname1;input;putlog _infile_;run;*/
|
/*data _null_;infile &fname1;input;putlog _infile_;run;*/
|
||||||
libname &libref1 JSON fileref=&fname1;
|
libname &libref1 JSON fileref=&fname1;
|
||||||
/* now get the followon link to list members */
|
/* now get the followon link to list members */
|
||||||
|
%local href;
|
||||||
|
%let href=0;
|
||||||
data _null_;
|
data _null_;
|
||||||
set &libref1..links;
|
set &libref1..links;
|
||||||
if rel='members' then call symputx('href',quote(trim(href)),'l');
|
if rel='members' then call symputx('href',quote("&base_uri"!!trim(href)),'l');
|
||||||
run;
|
run;
|
||||||
|
%if &href=0 %then %do;
|
||||||
|
%put NOTE:;%put NOTE- No members found in &root!!;%put NOTE-;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
%local fname2 libref2;
|
%local fname2 libref2;
|
||||||
%let fname2=%mf_getuniquefileref();
|
%let fname2=%mf_getuniquefileref();
|
||||||
%let libref2=%mf_getuniquelibref();
|
%let libref2=%mf_getuniquelibref();
|
||||||
|
|||||||
@@ -30,10 +30,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@@ -56,7 +55,7 @@
|
|||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
and &grant_type ne sas_services
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -73,7 +72,7 @@ options noquotelenmax;
|
|||||||
%let fname1=%mf_getuniquefileref();
|
%let fname1=%mf_getuniquefileref();
|
||||||
proc http method='GET' out=&fname1 &oauth_bearer
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
url="&base_uri/identities/groups/&group/members?limit=10000";
|
url="&base_uri/identities/groups/&group/members?limit=10000";
|
||||||
headers
|
headers
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
"Authorization"="Bearer &&&access_token_var"
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -29,10 +29,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@@ -54,7 +53,7 @@
|
|||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
and &grant_type ne sas_services
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -72,7 +71,7 @@ options noquotelenmax;
|
|||||||
|
|
||||||
proc http method='GET' out=&fname1 &oauth_bearer
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
url="&base_uri/identities/groups?limit=10000";
|
url="&base_uri/identities/groups?limit=10000";
|
||||||
headers
|
headers
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
"Authorization"="Bearer &&&access_token_var"
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
151
viya/mv_getjobcode.sas
Normal file
151
viya/mv_getjobcode.sas
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Extract the source code from a SAS Viya Job
|
||||||
|
@details Extracts the SAS code from a Job into a fileref or physical file.
|
||||||
|
Example:
|
||||||
|
|
||||||
|
%mv_getjobcode(
|
||||||
|
path=/Public/jobs
|
||||||
|
,name=some_job
|
||||||
|
,outfile=/tmp/some_job.sas
|
||||||
|
)
|
||||||
|
|
||||||
|
@param [in] access_token_var= The global macro variable to contain the access token
|
||||||
|
@param [in] grant_type= valid values:
|
||||||
|
* password
|
||||||
|
* authorization_code
|
||||||
|
* detect - will check if access_token exists, if not will use sas_services if
|
||||||
|
a SASStudioV session else authorization_code. Default option.
|
||||||
|
* sas_services - will use oauth_bearer=sas_services
|
||||||
|
@param [in] path= The SAS Drive path of the job
|
||||||
|
@param [in] name= The name of the job
|
||||||
|
@param [out] outref= A fileref to which to write the source code
|
||||||
|
@param [out] outfile= A file to which to write the source code
|
||||||
|
|
||||||
|
@version VIYA V.03.04
|
||||||
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mf_getplatform.sas
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mv_getfoldermembers.sas
|
||||||
|
@li ml_json.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mv_getjobcode(outref=0,outfile=0
|
||||||
|
,name=0,path=0
|
||||||
|
,contextName=SAS Job Execution compute context
|
||||||
|
,access_token_var=ACCESS_TOKEN
|
||||||
|
,grant_type=sas_services
|
||||||
|
);
|
||||||
|
%local oauth_bearer;
|
||||||
|
%if &grant_type=detect %then %do;
|
||||||
|
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||||
|
%else %let grant_type=sas_services;
|
||||||
|
%end;
|
||||||
|
%if &grant_type=sas_services %then %do;
|
||||||
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
|
%let &access_token_var=;
|
||||||
|
%end;
|
||||||
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
|
and &grant_type ne sas_services
|
||||||
|
)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=("&path"="0")
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Job Path not provided)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=("&name"="0")
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Job Name not provided)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue=("&outfile"="0" and "&outref"="0")
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Output destination (file or fileref) must be provided)
|
||||||
|
)
|
||||||
|
options noquotelenmax;
|
||||||
|
%local base_uri; /* location of rest apis */
|
||||||
|
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||||
|
data;run;
|
||||||
|
%local foldermembers;
|
||||||
|
%let foldermembers=&syslast;
|
||||||
|
%mv_getfoldermembers(root=&path
|
||||||
|
,access_token_var=&access_token_var
|
||||||
|
,grant_type=&grant_type
|
||||||
|
,outds=&foldermembers
|
||||||
|
)
|
||||||
|
%local joburi;
|
||||||
|
%let joburi=0;
|
||||||
|
data _null_;
|
||||||
|
set &foldermembers;
|
||||||
|
if name="&name" and uri=:'/jobDefinitions/definitions'
|
||||||
|
then call symputx('joburi',uri);
|
||||||
|
run;
|
||||||
|
%mp_abort(iftrue=("&joburi"="0")
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Job &path/&name not found)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* prepare request*/
|
||||||
|
%local fname1;
|
||||||
|
%let fname1=%mf_getuniquefileref();
|
||||||
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
|
url="&base_uri&joburi";
|
||||||
|
headers "Accept"="application/vnd.sas.job.definition+json"
|
||||||
|
%if &grant_type=authorization_code %then %do;
|
||||||
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
run;
|
||||||
|
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||||
|
%do;
|
||||||
|
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||||
|
%mp_abort(mac=&sysmacroname
|
||||||
|
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
%local fname2 fname3 fpath1 fpath2 fpath3;
|
||||||
|
%let fname2=%mf_getuniquefileref();
|
||||||
|
%let fname3=%mf_getuniquefileref();
|
||||||
|
%let fpath1=%sysfunc(pathname(&fname1));
|
||||||
|
%let fpath2=%sysfunc(pathname(&fname2));
|
||||||
|
%let fpath3=%sysfunc(pathname(&fname3));
|
||||||
|
|
||||||
|
/* compile the lua JSON module */
|
||||||
|
%ml_json()
|
||||||
|
/* read using LUA - this allows the code to be of any length */
|
||||||
|
data _null_;
|
||||||
|
file "&fpath3..lua";
|
||||||
|
put '
|
||||||
|
infile = io.open (sas.symget("fpath1"), "r")
|
||||||
|
outfile = io.open (sas.symget("fpath2"), "w")
|
||||||
|
io.input(infile)
|
||||||
|
local resp=json.decode(io.read())
|
||||||
|
local job=resp["code"]
|
||||||
|
outfile:write(job)
|
||||||
|
io.close(infile)
|
||||||
|
io.close(outfile)
|
||||||
|
';
|
||||||
|
run;
|
||||||
|
%inc "&fpath3..lua";
|
||||||
|
/* export to desired destination */
|
||||||
|
data _null_;
|
||||||
|
%if &outref=0 %then %do;
|
||||||
|
file "&outfile" lrecl=32767;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
file &outref;
|
||||||
|
%end;
|
||||||
|
infile &fname2;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
run;
|
||||||
|
filename &fname1 clear;
|
||||||
|
filename &fname2 clear;
|
||||||
|
filename &fname3 clear;
|
||||||
|
%mend;
|
||||||
267
viya/mv_getjoblog.sas
Normal file
267
viya/mv_getjoblog.sas
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Extract the log from a completed SAS Viya Job
|
||||||
|
@details Extracts log from a Viya job and writes it out to a fileref
|
||||||
|
|
||||||
|
To query the job, you need the URI. Sample code for achieving this
|
||||||
|
is provided below.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
First, compile the macros:
|
||||||
|
|
||||||
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
|
%inc mc;
|
||||||
|
|
||||||
|
Next, create a job (in this case, a web service):
|
||||||
|
|
||||||
|
filename ft15f001 temp;
|
||||||
|
parmcards4;
|
||||||
|
data ;
|
||||||
|
rand=ranuni(0)*1000;
|
||||||
|
do x=1 to rand;
|
||||||
|
y=rand*4;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
proc sort data=&syslast
|
||||||
|
by descending y;
|
||||||
|
run;
|
||||||
|
;;;;
|
||||||
|
%mv_createwebservice(path=/Public/temp,name=demo)
|
||||||
|
|
||||||
|
Execute it:
|
||||||
|
|
||||||
|
%mv_jobexecute(path=/Public/temp
|
||||||
|
,name=demo
|
||||||
|
,outds=work.info
|
||||||
|
)
|
||||||
|
|
||||||
|
Wait for it to finish, and grab the uri:
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.info;
|
||||||
|
if method='GET' and rel='self';
|
||||||
|
call symputx('uri',uri);
|
||||||
|
run;
|
||||||
|
|
||||||
|
Finally, fetch the log:
|
||||||
|
|
||||||
|
%mv_getjoblog(uri=&uri,outref=mylog)
|
||||||
|
|
||||||
|
This macro is used by the mv_jobwaitfor macro, which is generally a more
|
||||||
|
convenient way to wait for the job to finish before fetching the log.
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] access_token_var= The global macro variable to contain the access token
|
||||||
|
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||||
|
@param [in] grant_type= valid values:
|
||||||
|
@li password
|
||||||
|
@li authorization_code
|
||||||
|
@li detect - will check if access_token exists, if not will use sas_services if
|
||||||
|
a SASStudioV session else authorization_code. Default option.
|
||||||
|
@li sas_services - will use oauth_bearer=sas_services.
|
||||||
|
@param [in] uri= The uri of the running job for which to fetch the status,
|
||||||
|
in the format `/jobExecution/jobs/$UUID/state` (unquoted).
|
||||||
|
@param [out] outref= The output fileref to which to APPEND the log (is always
|
||||||
|
appended).
|
||||||
|
|
||||||
|
|
||||||
|
@version VIYA V.03.04
|
||||||
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mf_getplatform.sas
|
||||||
|
@li mf_existfileref.sas
|
||||||
|
@li ml_json.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mv_getjoblog(uri=0,outref=0
|
||||||
|
,contextName=SAS Job Execution compute context
|
||||||
|
,access_token_var=ACCESS_TOKEN
|
||||||
|
,grant_type=sas_services
|
||||||
|
,mdebug=0
|
||||||
|
);
|
||||||
|
%local oauth_bearer;
|
||||||
|
%if &grant_type=detect %then %do;
|
||||||
|
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||||
|
%else %let grant_type=sas_services;
|
||||||
|
%end;
|
||||||
|
%if &grant_type=sas_services %then %do;
|
||||||
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
|
%let &access_token_var=;
|
||||||
|
%end;
|
||||||
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
|
and &grant_type ne sas_services
|
||||||
|
)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* validation in datastep for better character safety */
|
||||||
|
%local errmsg errflg;
|
||||||
|
data _null_;
|
||||||
|
uri=symget('uri');
|
||||||
|
if length(uri)<12 then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||||
|
end;
|
||||||
|
if scan(uri,-1)='state' or scan(uri,1) ne 'jobExecution' then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',
|
||||||
|
"URI should be in format /jobExecution/jobs/$$$$UUID$$$$"
|
||||||
|
!!" but is actually like: &uri",'l');
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&errflg=1)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(&errmsg)
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&outref=0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Output fileref should be provided)
|
||||||
|
)
|
||||||
|
|
||||||
|
%if %mf_existfileref(&outref) ne 1 %then %do;
|
||||||
|
filename &outref temp;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
options noquotelenmax;
|
||||||
|
%local base_uri; /* location of rest apis */
|
||||||
|
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||||
|
|
||||||
|
/* prepare request*/
|
||||||
|
%local fname1;
|
||||||
|
%let fname1=%mf_getuniquefileref();
|
||||||
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
|
url="&base_uri&uri";
|
||||||
|
headers
|
||||||
|
%if &grant_type=authorization_code %then %do;
|
||||||
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
run;
|
||||||
|
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||||
|
%do;
|
||||||
|
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||||
|
%mp_abort(mac=&sysmacroname
|
||||||
|
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
%local fname2 fname3 fpath1 fpath2 fpath3;
|
||||||
|
%let fname2=%mf_getuniquefileref();
|
||||||
|
%let fname3=%mf_getuniquefileref();
|
||||||
|
%let fpath1=%sysfunc(pathname(&fname1));
|
||||||
|
%let fpath2=%sysfunc(pathname(&fname2));
|
||||||
|
%let fpath3=%sysfunc(pathname(&fname3));
|
||||||
|
|
||||||
|
/* compile the lua JSON module */
|
||||||
|
%ml_json()
|
||||||
|
/* read using LUA - this allows the code to be of any length */
|
||||||
|
data _null_;
|
||||||
|
file "&fpath3..lua";
|
||||||
|
put '
|
||||||
|
infile = io.open (sas.symget("fpath1"), "r")
|
||||||
|
outfile = io.open (sas.symget("fpath2"), "w")
|
||||||
|
io.input(infile)
|
||||||
|
local resp=json.decode(io.read())
|
||||||
|
local logloc=resp["logLocation"]
|
||||||
|
outfile:write(logloc)
|
||||||
|
io.close(infile)
|
||||||
|
io.close(outfile)
|
||||||
|
';
|
||||||
|
run;
|
||||||
|
%inc "&fpath3..lua";
|
||||||
|
/* get log path*/
|
||||||
|
%let errflg=1;
|
||||||
|
%let errmsg=No entry in &fname2 fileref;
|
||||||
|
data _null_;
|
||||||
|
infile &fname2;
|
||||||
|
input;
|
||||||
|
uri=_infile_;
|
||||||
|
if length(uri)<12 then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||||
|
end;
|
||||||
|
if scan(uri,1) ne 'files' or scan(uri,2) ne 'files' then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',
|
||||||
|
"URI should be in format /files/files/$$$$UUID$$$$"
|
||||||
|
!!" but is actually like: &uri",'l');
|
||||||
|
end;
|
||||||
|
call symputx('errflg',0,'l');
|
||||||
|
call symputx('logloc',uri,'l');
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&errflg=1)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(&errmsg)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* we have a log uri - now fetch the log */
|
||||||
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
|
url="&base_uri&logloc/content";
|
||||||
|
headers
|
||||||
|
%if &grant_type=authorization_code %then %do;
|
||||||
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
%end;
|
||||||
|
;
|
||||||
|
run;
|
||||||
|
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||||
|
%do;
|
||||||
|
data _null_;infile &fname1;input;putlog _infile_;run;
|
||||||
|
%mp_abort(mac=&sysmacroname
|
||||||
|
,msg=%str(logfetch: &SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
file "&fpath3..lua";
|
||||||
|
put '
|
||||||
|
infile = io.open (sas.symget("fpath1"), "r")
|
||||||
|
outfile = io.open (sas.symget("fpath2"), "w")
|
||||||
|
io.input(infile)
|
||||||
|
local resp=json.decode(io.read())
|
||||||
|
for i, v in pairs(resp["items"]) do
|
||||||
|
outfile:write(v.line,"\n")
|
||||||
|
end
|
||||||
|
io.close(infile)
|
||||||
|
io.close(outfile)
|
||||||
|
';
|
||||||
|
run;
|
||||||
|
%inc "&fpath3..lua";
|
||||||
|
|
||||||
|
/* write log out to the specified fileref */
|
||||||
|
data _null_;
|
||||||
|
infile &fname2 end=last;
|
||||||
|
file &outref mod;
|
||||||
|
if _n_=1 then do;
|
||||||
|
put "/** SASJS Viya Job Log Extract start: &uri **/";
|
||||||
|
end;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
putlog _infile_;
|
||||||
|
%end;
|
||||||
|
if last then do;
|
||||||
|
put "/** SASJS Viya Job Log Extract end: &uri **/";
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &mdebug=0 %then %do;
|
||||||
|
filename &fname1 clear;
|
||||||
|
filename &fname2 clear;
|
||||||
|
filename &fname3 clear;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%put _local_;
|
||||||
|
%end;
|
||||||
|
%mend;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
169
viya/mv_getjobstate.sas
Normal file
169
viya/mv_getjobstate.sas
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Extract the status from a running SAS Viya job
|
||||||
|
@details Extracts the status from a running job and appends it to an output
|
||||||
|
dataset with the following structure:
|
||||||
|
|
||||||
|
| uri | state | timestamp |
|
||||||
|
|---------------------------------------------------------------|---------|--------------------|
|
||||||
|
| /jobExecution/jobs/5cebd840-2063-42c1-be0c-421ec3e1c175/state | running | 15JAN2021:12:35:08 |
|
||||||
|
|
||||||
|
To query the running job, you need the URI. Sample code for achieving this
|
||||||
|
is provided below.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
First, compile the macros:
|
||||||
|
|
||||||
|
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
|
%inc mc;
|
||||||
|
|
||||||
|
Next, create a long running job (in this case, a web service):
|
||||||
|
|
||||||
|
filename ft15f001 temp;
|
||||||
|
parmcards4;
|
||||||
|
data ;
|
||||||
|
rand=ranuni(0)*1000;
|
||||||
|
do x=1 to rand;
|
||||||
|
y=rand*4;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
data _null_;
|
||||||
|
call sleep(5,1);
|
||||||
|
run;
|
||||||
|
;;;;
|
||||||
|
%mv_createwebservice(path=/Public/temp,name=demo)
|
||||||
|
|
||||||
|
Execute it, grab the uri, and finally, check the job status:
|
||||||
|
|
||||||
|
%mv_jobexecute(path=/Public/temp
|
||||||
|
,name=demo
|
||||||
|
,outds=work.info
|
||||||
|
)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.info;
|
||||||
|
if method='GET' and rel='state';
|
||||||
|
call symputx('uri',uri);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mv_getjobstate(uri=&uri,outds=results)
|
||||||
|
|
||||||
|
You can run this macro as part of a loop to await the final 'completed' status.
|
||||||
|
The full list of status values is:
|
||||||
|
|
||||||
|
@li idle
|
||||||
|
@li pending
|
||||||
|
@li running
|
||||||
|
@li canceled
|
||||||
|
@li completed
|
||||||
|
@li failed
|
||||||
|
|
||||||
|
If you have one or more jobs that you'd like to wait for completion you can
|
||||||
|
also use the [mv_jobwaitfor](/mv__jobwaitfor_8sas.html) macro.
|
||||||
|
|
||||||
|
@param [in] access_token_var= The global macro variable to contain the access token
|
||||||
|
@param [in] grant_type= valid values:
|
||||||
|
@li password
|
||||||
|
@li authorization_code
|
||||||
|
@li detect - will check if access_token exists, if not will use sas_services if
|
||||||
|
a SASStudioV session else authorization_code. Default option.
|
||||||
|
@li sas_services - will use oauth_bearer=sas_services.
|
||||||
|
@param [in] uri= The uri of the running job for which to fetch the status,
|
||||||
|
in the format `/jobExecution/jobs/$UUID/state` (unquoted).
|
||||||
|
@param [out] outds= The output dataset in which to APPEND the status. Three
|
||||||
|
fields are appended: `CHECK_TM`, `URI` and `STATE`. If the dataset does not
|
||||||
|
exist, it is created.
|
||||||
|
|
||||||
|
|
||||||
|
@version VIYA V.03.04
|
||||||
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mf_getplatform.sas
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mv_getjobstate(uri=0,outds=work.mv_getjobstate
|
||||||
|
,contextName=SAS Job Execution compute context
|
||||||
|
,access_token_var=ACCESS_TOKEN
|
||||||
|
,grant_type=sas_services
|
||||||
|
);
|
||||||
|
%local oauth_bearer;
|
||||||
|
%if &grant_type=detect %then %do;
|
||||||
|
%if %symexist(&access_token_var) %then %let grant_type=authorization_code;
|
||||||
|
%else %let grant_type=sas_services;
|
||||||
|
%end;
|
||||||
|
%if &grant_type=sas_services %then %do;
|
||||||
|
%let oauth_bearer=oauth_bearer=sas_services;
|
||||||
|
%let &access_token_var=;
|
||||||
|
%end;
|
||||||
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
|
and &grant_type ne sas_services
|
||||||
|
)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Invalid value for grant_type: &grant_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* validation in datastep for better character safety */
|
||||||
|
%local errmsg errflg;
|
||||||
|
data _null_;
|
||||||
|
uri=symget('uri');
|
||||||
|
if length(uri)<12 then do;
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',"URI is invalid (too short) - '&uri'",'l');
|
||||||
|
end;
|
||||||
|
if scan(uri,-1) ne 'state' or scan(uri,1) ne 'jobExecution' then do;
|
||||||
|
|
||||||
|
call symputx('errflg',1);
|
||||||
|
call symputx('errmsg',
|
||||||
|
"URI should be in format /jobExecution/jobs/$$$$UUID$$$$/state"
|
||||||
|
!!" but is actually like: &uri",'l');
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(iftrue=(&errflg=1)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(&errmsg)
|
||||||
|
)
|
||||||
|
|
||||||
|
options noquotelenmax;
|
||||||
|
%local base_uri; /* location of rest apis */
|
||||||
|
%let base_uri=%mf_getplatform(VIYARESTAPI);
|
||||||
|
|
||||||
|
%local fname0;
|
||||||
|
%let fname0=%mf_getuniquefileref();
|
||||||
|
|
||||||
|
proc http method='GET' out=&fname0 &oauth_bearer url="&base_uri/&uri";
|
||||||
|
headers "Accept"="text/plain"
|
||||||
|
%if &grant_type=authorization_code %then %do;
|
||||||
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
|
%end; ;
|
||||||
|
run;
|
||||||
|
%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
|
||||||
|
%do;
|
||||||
|
data _null_;infile &fname0;input;putlog _infile_;run;
|
||||||
|
%mp_abort(mac=&sysmacroname
|
||||||
|
,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data;
|
||||||
|
format uri $128. state $32. timestamp datetime19.;
|
||||||
|
infile &fname0;
|
||||||
|
uri="&uri";
|
||||||
|
timestamp=datetime();
|
||||||
|
input;
|
||||||
|
state=_infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc append base=&outds data=&syslast;
|
||||||
|
run;
|
||||||
|
|
||||||
|
filename &fname0 clear;
|
||||||
|
|
||||||
|
%mend;
|
||||||
@@ -3,14 +3,13 @@
|
|||||||
@brief deprecated - replaced by mv_tokenauth.sas
|
@brief deprecated - replaced by mv_tokenauth.sas
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
<h4> Dependencies </h4>
|
|
||||||
@li mv_tokenauth.sas
|
@li mv_tokenauth.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mv_getrefreshtoken(client_id=someclient
|
%macro mv_getrefreshtoken(client_id=someclient
|
||||||
,client_secret=somesecret
|
,client_secret=somesecret
|
||||||
,grant_type=authorization_code
|
,grant_type=authorization_code
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
/**
|
/**
|
||||||
@file mv_getusergroups.sas
|
@file mv_getusergroups.sas
|
||||||
@brief Creates a dataset with a list of groups for a particular user
|
@brief Creates a dataset with a list of groups for a particular user
|
||||||
@details First, be sure you have an access token (which requires an app token).
|
@details If using outside of Viya SPRE, then an access token is needed.
|
||||||
|
|
||||||
Using the macros here:
|
Compile the macros here:
|
||||||
|
|
||||||
filename mc url
|
filename mc url
|
||||||
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||||
%inc mc;
|
%inc mc;
|
||||||
|
|
||||||
An administrator needs to set you up with an access code:
|
Then run the macro!
|
||||||
|
|
||||||
%mv_registerclient(outds=client)
|
|
||||||
|
|
||||||
Navigate to the url from the log (opting in to the groups) and paste the
|
|
||||||
access code below:
|
|
||||||
|
|
||||||
%mv_tokenauth(inds=client,code=wKDZYTEPK6)
|
|
||||||
|
|
||||||
Now we can run the macro!
|
|
||||||
|
|
||||||
%mv_getusergroups(&sysuserid,outds=users)
|
%mv_getusergroups(&sysuserid,outds=users)
|
||||||
|
|
||||||
@@ -29,10 +20,9 @@
|
|||||||
|
|
||||||
|
|
||||||
@version VIYA V.03.04
|
@version VIYA V.03.04
|
||||||
@author Allan Bowe
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
<h4> Dependencies </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mf_getplatform.sas
|
@li mf_getplatform.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
@@ -55,7 +45,7 @@
|
|||||||
%let &access_token_var=;
|
%let &access_token_var=;
|
||||||
%end;
|
%end;
|
||||||
%put &sysmacroname: grant_type=&grant_type;
|
%put &sysmacroname: grant_type=&grant_type;
|
||||||
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
|
||||||
and &grant_type ne sas_services
|
and &grant_type ne sas_services
|
||||||
)
|
)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
@@ -73,7 +63,7 @@ options noquotelenmax;
|
|||||||
|
|
||||||
proc http method='GET' out=&fname1 &oauth_bearer
|
proc http method='GET' out=&fname1 &oauth_bearer
|
||||||
url="&base_uri/identities/users/&user/memberships?limit=10000";
|
url="&base_uri/identities/users/&user/memberships?limit=10000";
|
||||||
headers
|
headers
|
||||||
%if &grant_type=authorization_code %then %do;
|
%if &grant_type=authorization_code %then %do;
|
||||||
"Authorization"="Bearer &&&access_token_var"
|
"Authorization"="Bearer &&&access_token_var"
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user