1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-12 15:04:36 +00:00

Compare commits

...

131 Commits

Author SHA1 Message Date
e3c333ea39 feat: adding mm_deletelibrary.sas, a macro to easily delete a SAS Library from metadata. Documentation: https://core.sasjs.io/mm__deletelibrary_8sas.html 2021-03-30 00:02:20 +02:00
ae72446f85 chore: updating all.sas 2021-03-30 00:01:43 +02:00
2b6bf4bd02 chore: doxy fixes 2021-03-30 00:01:08 +02:00
6dbb3760e0 Merge branch 'main' of github.com:sasjs/core into main 2021-03-29 23:02:33 +02:00
200d9a5761 chore: doxy updates 2021-03-29 23:02:17 +02:00
Allan Bowe
01a9a5b823 Update README.md 2021-03-25 20:09:24 +01:00
Allan Bowe
35eadd0e9d Update README.md 2021-03-25 15:45:28 +01:00
5cdca95216 chore: vscode properties 2021-03-24 19:38:02 +01:00
Allan Bowe
81b75a32ed chore: latest sasjs version 2021-03-24 18:34:58 +00:00
b7f5a2ec00 chore: devops rules, updated gitignore and git hook 2021-03-22 10:14:52 +01:00
db859bbf1d chore: adding a git hook to prevent sas files from appearing with capital letters. To install, just run
> @sasjs/core@1.0.0 postinstall
> node-git-hooks

Installing Git hooks...

up to date, audited 2 packages in 1s

found 0 vulnerabilities.
2021-03-21 23:59:21 +01:00
27b56e8efd set executable 2021-03-21 23:33:19 +01:00
28ea218d02 chore: adding pre-commit hook 2021-03-21 22:24:46 +01:00
f356e1f351 chore: updating CONTRIBUTING, preventing files with spaces being added, adding git hooks package 2021-03-21 22:23:50 +01:00
4b375e0b8c fix: leading zero in time component of mf_uid() 2021-03-19 20:34:16 +01:00
7db207dd1c chore: updating all.sas 2021-03-19 20:29:35 +01:00
Allan Bowe
ffdfc57aa6 Merge pull request #9 from tmoody/main
fix: Closes issue #8. Optionally raise SYSCC and exit early on job no…
2021-03-17 17:32:35 +01:00
Trevor Moody
6fc8408988 fix: Syntax correction for raise_err parameter 2021-03-17 16:31:47 +00:00
Trevor Moody
eeb25fa5bc fix: Closes issue #8. Optionally raise SYSCC and exit early on job not completing successfully 2021-03-17 16:27:10 +00:00
Allan Bowe
521d128afe chore: gitpod file 2021-03-17 09:21:16 +00:00
Allan Bowe
0135dd6c8f chore: updating gitpod files 2021-03-17 07:40:48 +00:00
Allan Bowe
1b66c59dc0 feat: adding gitpod.yml with sasjs code extension 2021-03-15 09:21:27 +00:00
96be5c65dc fix: incorrect assignment of 0D as LF and 0A as CR, fixed in both mv_createwebservice.sas and mv_createjob.sas 2021-03-08 22:06:57 +01:00
8f6ef569e1 fix: adding filename clear statements 2021-03-07 18:47:41 +01:00
ff45c5a8b8 fix: ensuring unique filerefs in mm_updatestpsourcecode macro. Marking previous placeholders as deprecated for future release. 2021-03-07 18:46:32 +01:00
fb5f1c820a chore: delisting the sasjs/cli dependency 2021-03-07 16:43:46 +01:00
c0e33175cf feat: mm_getfoldercontents.sas to fetch immediate children for any folder including a root level folder 2021-03-07 16:19:25 +01:00
2bfa72f48f feat: mv_createjob macro for creating a viya job 2021-03-07 11:46:21 +01:00
fdc2e8ac8a chore: header section for mp_lib2cards.sas 2021-03-04 14:20:04 +01:00
2a894419ab feat: updating mp_lib2cards to enable a single file to be created with all the tables for a particular library 2021-03-01 17:49:26 +01:00
58bfc7b4aa chore: updating doxy formatting to include folder descriptions, also updating headers for some macros 2021-03-01 17:48:44 +01:00
818c0f5eae fix: lua feature discovery logic fix 2021-02-21 17:15:49 +01:00
dff9e2f387 chore: doxy updates 2021-02-21 17:15:17 +01:00
6c9256e097 chore: adding a CONTRIBUTING.md file 2021-02-12 22:07:04 +01:00
0631a05a78 chore(docs): adding a homepage to the doc site and integrating with the sasjs doc command. See https://core.sasjs.io 2021-02-10 00:12:49 +01:00
268bdca4e0 chore: adding sasjsconfig schema file and updating package.json with SASjs CLI devDependency (used to generate doxygen docs) 2021-02-07 21:47:34 +01:00
Allan Bowe
e38f331ad5 Merge pull request #6 from sasjs/sasjsdoc
initial commit
2021-02-04 15:48:20 +02:00
8d64b30419 fix: adding a one second pause between every SAS Job Request in mv_jobflow.sas 2021-02-04 14:12:02 +01:00
4a6c8ffbb3 fix: replacing WARNING with %str(WARN)ING to avoid being caught in searches for mf_getattrn 2021-01-31 18:34:10 +01:00
b5c86e7031 fix: mv_jobflow param mixup, not all jobs were running (fixed now). Also fixed doc formatting, removed unnecessary logging, and fixed a debug switch. 2021-01-31 17:58:13 +01:00
9783edd0e3 feat: adding outref option to mv_jobflow so that logs of submitted jobs can be captured. Also making the context name and flow id optional in the input table, for ease of use. 2021-01-29 12:56:12 +01:00
961728a987 chore: updating header link 2021-01-27 00:28:26 +01:00
4b34322d94 feat: mv_getjoblog.sas macro - will fetch a SAS log from an executed SAS Viya log and append it to a fileref.
mv_jobwaitfor is updated to allow the log to be fetched for all the submitted jobs.
2021-01-27 00:14:21 +01:00
8bb83deede fix: updating return codes 2021-01-26 16:04:44 +01:00
79c81aa8a4 feat: mf_existfileref macro 2021-01-26 16:00:23 +01:00
bbbcf7d550 chore: updating the docs for mf_getquotedstr 2021-01-23 13:37:15 +02:00
82184bc6be fix: adding quit statement so that exit loop would work on step boundary 2021-01-21 21:55:07 +02:00
efc731cfaa feat: mp_testjob macro for running arbitrary long jobs 2021-01-21 21:48:05 +02:00
da9a74ee14 chore: updating doxy headers 2021-01-21 21:47:41 +02:00
94762d9381 feat: mv_jobflow macro - enables a SAS program to kick off multiple waves of SAS Viya jobs, and to limit those waves by a maximum number of parallel (concurrent) running jobs. 2021-01-16 21:34:17 +02:00
03d9d805ff fix: adding support for jobparams in output table for mv_jobwaitfor 2021-01-16 20:43:15 +02:00
94416028b7 fix: adding ACTION parameter to mv_jobwaitfor - can now wait for ANY or ALL jobs to finish 2021-01-16 19:08:38 +02:00
6cf5d4ef28 chore: updating the header description 2021-01-15 23:12:38 +02:00
e4ceaecfb2 feat: adding mv_getjobstate macro to fetch the state of a running SAS Viya job 2021-01-15 13:02:53 +02:00
Allan Bowe
2eb246c543 fix: removing favicon file 2021-01-14 18:07:42 +01:00
d9954ae777 fix: renegade comma 2021-01-14 16:55:17 +02:00
364dc9f07f feat: adding _program value to mv_jobexecute.sas 2021-01-14 16:37:58 +02:00
Saad Jutt
fbd8196230 chore: Doxyfile updated + others formatted 2021-01-09 13:42:55 +05:00
Allan Bowe
5720caaf86 initial commit 2021-01-08 14:28:31 +00:00
d96125c3cf fix: mv_jobwaitfor 2021-01-05 17:14:03 +00:00
506695be56 feat: mv_jobwaitfor macro, similar to waitfor statement (in concept) - will wait for ALL of a set of viya jobs to finish executing 2021-01-05 14:41:27 +00:00
Allan Bowe
45f858db15 fix: scope of json var, brining the %inc _inside_ the macro 2021-01-03 22:35:56 +00:00
Allan Bowe
b4d97a063a fix: doc update for lua files, plus leftover reference in code 2021-01-03 22:30:41 +00:00
Allan Bowe
4df8f3b4c2 feat: mv_getjobcode macro, introducing LUA macros 2021-01-03 22:16:11 +00:00
Allan Bowe
11aa484996 chore: documentation 2020-12-30 17:06:04 +00:00
Allan Bowe
b9fd79bd5e fix: debugging in mm_assignlib 2020-12-25 16:58:30 +00:00
Allan Bowe
1beb30d0ff fix: updating <h4> Dependencies </h4> in header to be <h4> SAS Macros </h4> in line with the updated SASjs compilation process (which distinguishes between SAS Macro and SAS Program dependencies)
BREAKING CHANGE - this doesn't break anything in the framework but I know of at least one old project that uses the <h4> Dependencies </h4> tag to perform backend compilation, so am bumping the version to be safe (looking at you, Chris
2020-12-25 10:36:19 +00:00
Allan Bowe
e334ea9b85 chore: all.sas update 2020-12-25 10:22:44 +00:00
Allan Bowe
c090c8d53b feat: simple macro to test the write speed for a library (very very basic) 2020-12-25 10:18:02 +00:00
Allan Bowe
659339bd98 fix: more comments in mp_prevobs 2020-12-25 10:15:03 +00:00
Allan Bowe
4c333ae7b3 feat: mp_prevobs.sas macro 2020-12-23 00:33:59 +00:00
b3a8b4323e fix: adding new mp_ds2csv macro 2020-12-16 17:11:31 +01:00
0592206f2d patch: doxy formatting 2020-12-03 22:44:08 +01:00
bedc2a443a fix: @cond on new line to prevent parsing issues in sasjs cli 2020-12-02 08:09:51 +01:00
6f86ed62a2 chore: doxy formatting 2020-11-29 22:03:20 +01:00
def0cc8476 fix: adding outds and parameters to mv_jobexecute 2020-11-29 21:55:21 +01:00
3a9029557e chore: doxygen updates 2020-11-29 21:06:39 +01:00
9dc3bcd513 fix: updating all.sas 2020-11-29 13:58:04 +01:00
2bcf6346ac fix: upgrading to latest doxygen 2020-11-29 13:57:44 +01:00
0eccc169f5 feat: adding mv_jobexecute macro (and a fix for mv_getfoldermembers where there are no members) 2020-11-29 13:56:51 +01:00
493639fe4a fix: composite PK 2020-11-26 01:26:16 +01:00
4987d2fbbc fix: missing dependency in mp_getdbml 2020-11-26 01:11:33 +01:00
1a35b357d6 feat: mp_tree macro 2020-11-25 23:21:07 +01:00
a7792d93e4 feat: mf_isdir macro 2020-11-25 22:35:04 +01:00
541dc31ad0 feat: mp_getdbml.sas macro for generating DBML for one or more SAS Libraries 2020-11-25 16:37:42 +01:00
abccafab7b feat: adding filref option to mp_streamfile.sas 2020-11-16 11:08:01 +01:00
f6cec012da fix: removing unnecessary cond flags in mf_abort, adding abort logic in mp_csv2ds 2020-11-08 22:04:25 +01:00
d51be73017 fix: macro param and extra log info in mp_csv2ds 2020-11-03 09:43:47 +01:00
cafffbb509 fix: adding a period to enable formats such as anydtdtme 2020-11-02 17:26:59 +01:00
a88efacfab fix: making view an option so that existence can be checked for 2020-11-02 17:15:29 +01:00
cc7cc55022 feat: mp_csv2ds macro for importing a CSV using a SAS table to provide a template (eg for lengths / types etc) 2020-11-02 15:44:45 +01:00
15687be5d6 fix: tidy up of SAS flavour DDL 2020-10-30 11:43:15 +01:00
d9a82c0bdf fix: incorrect filepath when using filerefs in mp_dirlist 2020-10-29 12:08:07 +01:00
6f06e5540d feat: adding fileref support for mp_dirlist, as well as a directory column on the output dataset 2020-10-29 11:30:15 +01:00
6b782a4fa2 chore: adding sitemap 2020-10-18 01:00:35 +02:00
efe4709dde chore: formatting in mp_guesspk 2020-10-14 16:42:04 +02:00
5cb41041d9 fix: upcase showlog value in mp_getddl() to allow lowercase user entries 2020-10-13 11:20:30 +02:00
f50cb03fd3 fix: mp_ds2cards was failing when the maxobs was less than the number of variables. SQL maxobs option is now reset. 2020-10-12 18:28:50 +02:00
ac46489f11 fix: enabling mp_abort.sas to work in Viya when useComputeApi is true (and the SYS_JES_JOB_URI is empty) 2020-10-11 00:34:08 +02:00
vrh
5e45701e74 fix: extra debug info in mp_searchdata, as well as named literal support 2020-10-02 22:34:36 +02:00
vrh
8caaacd9f0 docs: comment fix 2020-09-28 09:22:29 +02:00
vrh
91983e0a91 fix: trim edge cases and return of the register client url 2020-09-11 00:38:37 +02:00
vrh
7b72f0ac94 fix: alternative base_uri option on tokenauth macro, also description update for listclients 2020-09-10 09:30:09 +02:00
vrh
3eae34d8b7 fix: adding base_uri in all calls to cover instances where viya calls are not on localhost 2020-09-07 14:01:39 +02:00
vrh
58358c916d chore: doc updates 2020-09-05 17:22:38 +02:00
vrh
578ef26cd5 fix: support for tab characters, closes https://github.com/macropeople/macrocore/issues/21 2020-09-04 09:51:11 +02:00
vrh
33189743cd fix: accidental end comment 2020-09-03 18:07:18 +02:00
vrh
459beff4fa feat: mf_trimstr macro 2020-09-03 17:49:44 +02:00
vrh
1c873afe57 fix: base_uri for mv_deletejes 2020-09-03 16:29:21 +02:00
Allan Bowe
2b683509ac Merge pull request #4 from sasjs/change
fix: base_uri
2020-09-03 16:26:48 +02:00
vrh
dcccd1491d fix: base_uri 2020-09-03 16:24:32 +02:00
vrh
8d9b84037c fix: base_uri missing 2020-09-03 16:06:47 +02:00
vrh
029c1a29ed feat: xlsx support in mp_streamfile 2020-08-18 23:09:13 +02:00
vrh
c4dbd5971f docs: formatting 2020-08-08 15:26:02 +02:00
vrh
40ac3bba9a merge 2020-08-08 14:32:12 +02:00
vrh
21946e74f1 feat: mm_gettableid macro 2020-08-08 14:29:55 +02:00
Allan Bowe
6443e2d2ef Merge pull request #3 from sasjs/mp_rafal
Fix: Unique Indexes Added
2020-08-05 13:57:00 +02:00
rafgag
17f03b7507 Fix: previous changes restored 2020-08-05 13:48:16 +02:00
rafgag
c6a18a4168 Fix: Unique Indexes Added 2020-08-05 12:42:27 +02:00
vrh
dd18d1d5a8 fix: adding schema to pg output 2020-08-04 21:19:00 +02:00
vrh
fc8a39bbca fix: quoting reserved words 2020-08-04 18:45:00 +02:00
vrh
8beec7dc19 fix: quoting for instances of reserved words in pgsql 2020-08-04 18:13:22 +02:00
vrh
e3f6cb7b45 chore: editor config file 2020-08-04 14:45:54 +02:00
Allan Bowe
041aff9bc0 Merge pull request #2 from rafgag/mp_rafal
FEAT: adding Primary Key constraints
2020-08-04 12:00:28 +02:00
rafgag
1986732573 Update mp_getddl.sas 2020-08-04 12:00:05 +02:00
rafgag
958f509894 Update mp_getddl.sas 2020-08-04 11:34:19 +02:00
rafgag
1373957031 Update mp_getddl.sas 2020-08-04 11:30:12 +02:00
rafgag
8466acc7a7 Update mp_getddl.sas 2020-08-04 11:25:59 +02:00
rafgag
45e646565f FEAT: adding Primary Key constraints 2020-08-03 16:51:14 +02:00
Allan Bowe
9d9a72220f fix: syscc value (not length) assertion 2020-07-30 02:42:13 +02:00
Allan Bowe
53865a3909 feat: Postgres support for getddl macro 2020-07-29 12:32:54 +02:00
134 changed files with 7431 additions and 5411 deletions

12
.editorconfig Normal file
View 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

44
.githooks/pre-commit Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/bash
#
# A hook script to verify that no filenames with capital letters are committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# Go through all the changed files (except for deleted and unmerged)
# Save exit code of last executed action
exit_code=0
# Check if file is one of SAS|DDL|CSV|SH and check for uppercase letters
mime_pattern="\.(sas|ddl|csv|sh)"
# Check for capital letters only in file names
extra_pattern="(^|/)[^/]*([A-Z]+)[^/]*\.[A-Za-z]{3}$"
# Grep git diff of files to commit
files=$( git diff --cached --find-copies --find-renames --name-only --diff-filter=ACMRTXBU |
grep -Ei "$mime_pattern" |
grep -E "$extra_pattern" )
echo "$files"
if [[ -n "$files" ]];
then
echo
echo "Found files that contain capital letters."
echo "Please rename the following files in lowercase, and commit again:"
for file in $files; do
echo -e '- \E[0;32m'"$file"'\033[0m'
done
# Abort commit
exit_code=1
fi
if [ "$exit_code" == "0" ]; then
echo
echo -e '\033[1m'"Pre-commit validation Passed"'\033[0m'
echo
else
echo
echo -e '\033[1m'"Commit Aborted!"'\033[0m'
echo
fi
exit $exit_code

8
.gitignore vendored
View File

@@ -1 +1,9 @@
node_modules node_modules
.DS_Store
sasjsbuild/
# avoid filenames with spaces being committed to source control
**\ **
# ignore the mc_* files - containing macros for individual libraries
mc_*

10
.gitpod.dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM gitpod/workspace-full
RUN sudo apt-get update \
&& sudo apt-get install -y \
doxygen \
&& npm i -g npm@latest \
&& npm i -g @sasjs/cli \
&& npm i \
&& sudo rm -rf /var/lib/apt/lists/*

8
.gitpod.yml Normal file
View File

@@ -0,0 +1,8 @@
tasks:
- init: npm i && clear
image:
file: .gitpod.dockerfile
vscode:
extensions:
- sasjs.sasjs-for-vscode@1.6.0:V4hJpMtbpekMcPRNhh4SXQ==

9
.vscode/.editorconfig vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"search.exclude": {
"**/sasjsbuild/**": true,
"**/dist/**":true
},
"editor.insertSpaces": true,
"editor.tabSize": 2,
"trim_trailing_whitespace": true
}

10
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.detectIndentation": true,
"editor.formatOnSave": true,
"editor.rulers": [
80
],
"files.trimTrailingWhitespace": true
}

View File

@@ -1,83 +1,31 @@
# Contributing # Contributing
Contributions to the macrocore library are warmly welcomed! To avoid any Contributions are warmly welcomed! To avoid any misunderstandings, do please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before submitting a PR.
misunderstandings, do please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before submitting
a PR.
Please note we have a code of conduct, please follow it in all your interactions Please note we have a [code of conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/), please follow it in all your interactions with the project.
with the project.
# Environment Setup
This repository makes use of the [SASjs](https://sasjs.io) framework for code organisation, compilation, documentation, and deployment. The following tools are highly recommended:
* [NPM](https://sasjs.io/windows/#npm) - the runtime and dependency manager for [SASjs CLI](https://cli.sasjs.io) (batteries included)
* [VSCode](https://sasjs.io/windows/#vscode) - feature packed IDE for code editing (warning - highly effective!)
* [GIT](https://sasjs.io/windows/#git) - a safety net you cannot (and should not) do without.
For generating the documentation (`sasjs doc`) it is also necessary to install [doxygen](https://www.doxygen.nl/manual/install.html).
## Code of Conduct To get configured:
### Our Pledge 1. Clone the repository
2. Install local dependencies (`npm install`)
3. Install the SASjs CLI globally (`npm install -g @sasjs/cli`)
4. Add a target, and authentication (`npm add`). See [docs](https://cli.sasjs.io/add/).
In the interest of fostering an open and welcoming environment, we as To contribute:
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
### Our Standards 1. Create your feature branch (`git checkout -b myfeature`)
2. Make your change
3. Update the `all.sas` file (`python3 build.py`)
4. Push and make a PR
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at support@macropeople.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

2452
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@@ -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:

View File

@@ -1,6 +1,6 @@
# Macro Core # Macro Core
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of Application Development on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed. Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed.
You can download and compile them all in just two lines of SAS code: You can download and compile them all in just two lines of SAS code:
@@ -9,7 +9,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc; %inc mc;
``` ```
Documentation: https://sasjs.github.io/core.github.io/files.html Documentation: https://core.sasjs.io
# Components # Components
@@ -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:
@@ -63,7 +84,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
## File Properties ## File Properties
- filenames much match macro names - filenames much match macro names
- filenames must be lowercase - filenames must be lowercase, without spaces
- macro names must be lowercase - macro names must be lowercase
- one macro per file - one macro per file
- prefixes: - prefixes:
@@ -72,12 +93,13 @@ 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)
- individual lines should be no more than 80 characters long - individual lines should be no more than 80 characters long
- UTF-8 - UTF-8
- no trailing white space
## Header Properties ## Header Properties
@@ -91,17 +113,38 @@ 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
- Indentation = 2 spaces. No tabs! - Indentation = 2 spaces. No tabs!
- no trailing white space
- no invisible characters, other than spaces. If invisibles are needed, use hex literals.
- Macro variables should not have the trailing dot (`&var` not `&var.`) unless necessary to prevent incorrect resolution - Macro variables should not have the trailing dot (`&var` not `&var.`) unless necessary to prevent incorrect resolution
- The closing `%mend;` should not contain the macro name. - The closing `%mend;` should **not** contain the macro name.
- All macros should be defined with brackets, even if no variables are needed - ie `%macro x();` not `%macro x;` - All macros should be defined with brackets, even if no variables are needed - ie `%macro x();` not `%macro x;`
- 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;`
- The use of `quit;` for `proc sql` is optional unless you are looking to benefit from the timing statistics.
# General Notes # General Notes

3739
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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.
@@ -13,13 +13,14 @@
@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*/;
@@ -31,12 +32,16 @@
%put Supported features: PROCLUA; %put Supported features: PROCLUA;
%end; %end;
%else %if &feature=PROCLUA %then %do; %else %if &feature=PROCLUA %then %do;
/* https://blogs.sas.com/content/sasdummy/2015/08/03/using-lua-within-your-sas-programs */
%if &platform=SASVIYA %then 1; %if &platform=SASVIYA %then 1;
%else %if "&sysver"="9.3" or "&sysver"="9.4" %then 1; %else %if "&sysver"="9.2" or "&sysver"="9.3" %then 0;
%else 0; %else %if "&SYSVLONG" < "9.04.01M3" %then 0;
%else 1;
%end; %end;
%else %do; %else %do;
-1 -1
%put &sysmacroname: &feature not found; %put &sysmacroname: &feature not found;
%end; %end;
%mend; %mend;
/** @endcond */

27
base/mf_existfileref.sas Normal file
View 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;

View File

@@ -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 */
@@ -30,3 +31,5 @@
%end; %end;
%mend; %mend;
/** @endcond */

View File

@@ -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
@@ -54,3 +55,5 @@
%put Vars not found: &found; %put Vars not found: &found;
%end; %end;
%mend; %mend;
/** @endcond */

View File

@@ -23,7 +23,7 @@
%local dsid rc; %local dsid rc;
%let dsid=%sysfunc(open(&libds,is)); %let dsid=%sysfunc(open(&libds,is));
%if &dsid = 0 %then %do; %if &dsid = 0 %then %do;
%put WARNING: Cannot open %trim(&libds), system message below; %put %str(WARN)ING: Cannot open %trim(&libds), system message below;
%put %sysfunc(sysmsg()); %put %sysfunc(sysmsg());
-1 -1
%end; %end;

View File

@@ -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 */

View File

@@ -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;

View File

@@ -8,6 +8,13 @@
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)

View File

@@ -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 */

View File

@@ -1,7 +1,8 @@
/** /**
@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();
@@ -11,7 +12,7 @@
> 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.

View File

@@ -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.

View File

@@ -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
View 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;

View File

@@ -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
View 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;

View File

@@ -1,11 +1,11 @@
/** /**
@file @file
@brief Creates a Unique ID based on system time in a friendly format @brief Creates a unique ID based on system time in friendly format
@details format = YYYYMMDD_HHMMSSmmm_<sysjobid>_<3randomDigits> @details format = YYYYMMDD_HHMMSSmmm_<sysjobid>_<3randomDigits>
%put %mf_uid(); %put %mf_uid();
@version 9.2 @version 9.3
@author Allan Bowe @author Allan Bowe
**/ **/
@@ -14,7 +14,7 @@
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local today now; %local today now;
%let today=%sysfunc(today(),yymmddn8.); %let today=%sysfunc(today(),yymmddn8.);
%let now=%sysfunc(compress(%sysfunc(time(),time12.3),:.)); %let now=%sysfunc(compress(%sysfunc(time(),tod12.3),:.));
&today._&now._&sysjobid._%sysevalf(%sysfunc(ranuni(0))*999,CEIL) &today._&now._&sysjobid._%sysevalf(%sysfunc(ranuni(0))*999,CEIL)

View File

@@ -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

View File

@@ -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)
@@ -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 */

View File

@@ -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);
@@ -66,3 +68,4 @@
end; end;
run; run;
%mend; %mend;
/** @endcond */

View File

@@ -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

View File

@@ -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
View 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;

View File

@@ -20,6 +20,8 @@
%mp_dirlist(outds=cwdfileprops, getattrs=YES) %mp_dirlist(outds=cwdfileprops, getattrs=YES)
%mp_dirlist(fref=MYFREF)
@warning In a Unix environment, the existence of a named pipe will cause this @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
@@ -29,12 +31,14 @@
@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;
%if &fref=0 %then %do;
rc = filename(fref, "&path"); 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';
@@ -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);

View File

@@ -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

View File

@@ -3,25 +3,27 @@
@brief Create a CARDS file from a SAS dataset. @brief Create a CARDS file from a SAS dataset.
@details Uses dataset attributes to convert all data into datalines. @details Uses dataset attributes to convert all data into datalines.
Running the generated file will rebuild the original dataset. Running the generated file will rebuild the original dataset.
usage: Usage:
%mp_ds2cards(base_ds=sashelp.class %mp_ds2cards(base_ds=sashelp.class
, cards_file= "C:\temp\class.sas" , cards_file= "C:\temp\class.sas"
, maxobs=5) , maxobs=5)
stuff to add TODO:
- labelling the dataset - labelling the dataset
- explicity setting a unix LF - explicity setting a unix LF
- constraints / indexes etc - constraints / indexes etc
@param base_ds= Should be two level - eg work.blah. This is the table that @param [in] base_ds= Should be two level - eg work.blah. This is the table that
is converted to a cards file. is converted to a cards file.
@param tgt_ds= Table that the generated cards file would create. Optional - @param [in] tgt_ds= Table that the generated cards file would create. Optional -
if omitted, will be same as BASE_DS. if omitted, will be same as BASE_DS.
@param cards_file= Location in which to write the (.sas) cards file @param [out] cards_file= Location in which to write the (.sas) cards file
@param maxobs= to limit output to the first <code>maxobs</code> observations @param [in] maxobs= to limit output to the first <code>maxobs</code> observations
@param showlog= whether to show generated cards file in the SAS log (YES/NO) @param [in] showlog= whether to show generated cards file in the SAS log (YES/NO)
@param outencoding= provide encoding value for file statement (eg utf-8) @param [in] outencoding= provide encoding value for file statement (eg utf-8)
@param [in] append= If NO then will rebuild the cards file if it already exists,
otherwise will append to it. Used by the mp_lib2cards.sas macro.
@version 9.2 @version 9.2
@@ -34,6 +36,7 @@
,random_sample=NO ,random_sample=NO
,showlog=YES ,showlog=YES
,outencoding= ,outencoding=
,append=NO
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local i setds nvars; %local i setds nvars;
@@ -46,6 +49,8 @@
%if (&tgt_ds = ) %then %let tgt_ds=&base_ds; %if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
%if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.); %if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
%if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding"; %if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
%if ("&append" = "") %then %let append=;
%else %let append=mod;
/* get varcount */ /* get varcount */
%let nvars=0; %let nvars=0;
@@ -103,8 +108,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))"
@@ -173,7 +177,7 @@ data _null_;
run; run;
data _null_; data _null_;
file &cards_file. &outencoding lrecl=32767 termstr=nl; file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
length __attrib $32767; length __attrib $32767;
if _n_=1 then do; if _n_=1 then do;
put '/*******************************************************************'; put '/*******************************************************************';

58
base/mp_ds2csv.sas Normal file
View 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;

View File

@@ -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
View 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.
![dbml for sas](https://i.imgur.com/8T1tIZp.gif)
<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;

View File

@@ -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();
%global constraints_used;
data _null_;
length ctype $11 constraint_name_orig $256 constraints_used $5000;
set &colconst (where=(table_name="&curds" and constraint_type in ('PRIMARY','UNIQUE'))) end=last;
file &fref mod;
by constraint_type constraint_name;
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; %end;
; %else %if &flavour=PGSQL %then %do;
%local colinfo; %let colinfo=&syslast; column_name=catt('"',column_name,'"');
%local dsnlist; constraint_name=catt('"',constraint_name,'"');
select distinct upcase(memname) into: dsnlist %end;
separated by ' ' if first.constraint_name then do;
from &syslast; 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;
put ' ,constraint [' indxname '] PRIMARY KEY';
end;
else if unique='yes' then do;
/* add nonclustered in case of multiple unique indexes */ /* add nonclustered in case of multiple unique indexes */
put ' ,index [' indxname '] UNIQUE NONCLUSTERED'; put ' ,index [' indxname +(-1) '] UNIQUE NONCLUSTERED (';
put ' [' name +(-1) ']';
end; end;
put ' ('; else put ' ,[' name +(-1) ']';
put ' [' name ']';
end;
else put ' ,[' name ']';
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;

View File

@@ -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

View File

@@ -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

View File

@@ -1,21 +1,35 @@
/** /**
@file @file
@brief Convert all library members to CARDS files @brief Convert all library members to CARDS files
@details Gets list of members then calls the <code>%mp_ds2cards()</code> @details Gets list of members then calls the <code>%mp_ds2cards()</code> macro.
macro Usage:
usage:
%mp_lib2cards(lib=sashelp %mp_lib2cards(lib=sashelp
, outloc= C:\temp ) , outloc= C:\temp )
<h4> Dependencies </h4> The output will be one cards file in the `outloc` directory per dataset in the
input `lib` library. If the `outloc` directory does not exist, it is created.
To create a single SAS file with the first 1000 records of each table in a
library you could use this syntax:
%mp_lib2cards(lib=sashelp
, outloc= /tmp
, outfile= myfile.sas
, maxobs= 1000
)
<h4> SAS Macros </h4>
@li mf_mkdir.sas @li mf_mkdir.sas
@li mf_trimstr.sas
@li mp_ds2cards.sas @li mp_ds2cards.sas
@param lib= Library in which to convert all datasets @param [in] lib= Library in which to convert all datasets
@param outloc= Location in which to store output. Defaults to WORK library. @param [out] outloc= Location in which to store output. Defaults to WORK
Do not use a trailing slash (my/path not my/path/). No quotes. library. No quotes.
@param maxobs= limit output to the first <code>maxobs</code> observations @param [out] outfile= Optional output file NAME - if provided, then will create
a single output file instead of one file per input table.
@param [in] maxobs= limit output to the first <code>maxobs</code> observations
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@@ -25,6 +39,7 @@
,outloc=%sysfunc(pathname(work)) /* without trailing slash */ ,outloc=%sysfunc(pathname(work)) /* without trailing slash */
,maxobs=max ,maxobs=max
,random_sample=NO ,random_sample=NO
,outfile=0
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
/* Find the tables */ /* Find the tables */
@@ -36,6 +51,10 @@ select distinct lowcase(memname)
from dictionary.tables from dictionary.tables
where upcase(libname)="%upcase(&lib)"; where upcase(libname)="%upcase(&lib)";
/* trim trailing slash, if provided */
%let outloc=%mf_trimstr(&outloc,/);
%let outloc=%mf_trimstr(&outloc,\);
/* create the output directory */ /* create the output directory */
%mf_mkdir(&outloc) %mf_mkdir(&outloc)
@@ -43,9 +62,17 @@ select distinct lowcase(memname)
%do x=1 %to %sysfunc(countw(&memlist)); %do x=1 %to %sysfunc(countw(&memlist));
%let ds=%scan(&memlist,&x); %let ds=%scan(&memlist,&x);
%mp_ds2cards(base_ds=&lib..&ds %mp_ds2cards(base_ds=&lib..&ds
,cards_file="&outloc/&ds..sas"
,maxobs=&maxobs ,maxobs=&maxobs
,random_sample=&random_sample) ,random_sample=&random_sample
%if "&outfile" ne "0" %then %do;
,append=YES
,cards_file="&outloc/&outfile"
%end;
%else %do;
,append=NO
,cards_file="&outloc/&ds..sas"
%end;
)
%end; %end;
%mend; %mend;

88
base/mp_prevobs.sas Normal file
View 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;

View File

@@ -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;

View File

@@ -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
@@ -26,7 +26,7 @@
%if not (%mf_existds(&libds)) %then %do; %if not (%mf_existds(&libds)) %then %do;
data &libds (index=(key/unique)); data &libds (index=(key/unique));
length key $32 valc $256 valn 8 type $1; length key $64 valc $2048 valn 8 type $1;
call missing(of _all_); call missing(of _all_);
stop; stop;
run; run;

View File

@@ -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*/;
@@ -47,6 +49,7 @@
%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');
@@ -59,6 +62,19 @@
contentdisp="attachment; filename=&outname"; contentdisp="attachment; filename=&outname";
%end; %end;
%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";
%end;
%end;
%else %if &contentype=TEXT %then %do; %else %if &contentype=TEXT %then %do;
%if &platform=SASMETA %then %do; %if &platform=SASMETA %then %do;
data _null_; data _null_;
@@ -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
View 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;

View 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
View 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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +0,0 @@
ECHO on
rmdir /s /q C:\DEVELOPMENT\output
mkdir C:\DEVELOPMENT\output
doxygen.exe Doxyfile

View File

@@ -1,40 +0,0 @@
#!/bin/bash
####################################################################
# PROJECT: Macro Core Docs Build #
####################################################################
BUILD_FOLDER="/tmp/macrocore_docs"
# move to project root
cd ..
# create build directory
rm -rf $BUILD_FOLDER
mkdir $BUILD_FOLDER
# copy relevant files
cp -r base $BUILD_FOLDER
cp -r meta $BUILD_FOLDER
cp -r metax $BUILD_FOLDER
cp -r viya $BUILD_FOLDER
cp -r doxy $BUILD_FOLDER
cp main.dox $BUILD_FOLDER
cp doxy/Doxyfile $BUILD_FOLDER
# update Doxyfile and generate
cd $BUILD_FOLDER
echo "OUTPUT_DIRECTORY=$BUILD_FOLDER/out" >> $BUILD_FOLDER/Doxyfile
echo "INPUT+=main.dox" >> $BUILD_FOLDER/Doxyfile
doxygen Doxyfile
# refresh github pages site
git clone git@github.com:sasjs/core.github.io.git
cd core.github.io
git rm -r *
mv $BUILD_FOLDER/out/doxy/* .
echo 'core.sasjs.io' > CNAME
git add *
git commit -m "build.sh build on $(date +%F:%H:%M:%S)"
git push
echo "check it out: https://sasjs.github.io/core.github.io/files.html"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,22 +0,0 @@
<!--BEGIN GENERATE_TREEVIEW-->
<li class="footer"><b>$generatedby</b>
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/></a>
<i> For more information visit the </i> <a href="https://github.com/sasjs/core">Macro Core library</a>.</li>
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/>
<table width="100%"><tbody><tr><td>
For more information visit the <a href="https://github.com/sasjs/core">Macro Core library</a>.
</td><td><address class="footer"><small>
&copy;$year<br/>
$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-->
</body>
</html>

View File

@@ -1,72 +0,0 @@
<!-- HTML header for doxygen 1.8.14-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
<link REL="icon" HREF="https://sasjs.io/img/runningman.jpg">
$extrastylesheet
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea" style='background-color:white' >
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 26px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td>
<div id="projectname">
<!--a href=".">
<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>
</td>
<td>
<table style="padding-left: 2em;" cellspacing="0" cellpadding="0">
<tr><td> Production Ready Macros for SAS Application Developers</td></tr>
<tr><td><a href="https://github.com/sasjs/core">
https://github.com/sasjs/core
</a></td></tr>
</table>
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<div class="header">
<div class="headertitle">
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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;

View File

@@ -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
@@ -46,3 +51,14 @@
* 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_
*/

View File

@@ -1,5 +1,5 @@
/** /**
@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.
@@ -12,6 +12,7 @@
@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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -19,7 +19,7 @@
,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

View File

@@ -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

View File

@@ -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

View File

@@ -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

92
meta/mm_deletelibrary.sas Normal file
View File

@@ -0,0 +1,92 @@
/**
@file
@brief Deletes a library by Name
@details Used to delete a library.
Usage:
%* create a library in the home directory ;
%mm_createlibrary(
libname=My Temp Library,
libref=XXTEMPXX,
tree=/User Folders/&sysuserid,
directory=%sysfunc(pathname(work))
)
%* delete the library ;
%mm_deletelibrary(name=My Temp Library)
After running the above, the following will be shown in the log:
![](https://i.imgur.com/Y4Tog24.png)
@param [in] name= the name (not libref) of the library to be deleted
<h4> SAS Macros </h4>
@li mf_getuniquefileref.sas
@li mp_abort.sas
@version 9.4
@author Allan Bowe
**/
%macro mm_deletelibrary(
name=
)/*/STORE SOURCE*/;
/**
* Check if library exists and get uri
*/
data _null_;
length type uri $256;
rc=metadata_resolve("omsobj:SASLibrary?@Name='&name'",type,uri);
call symputx('checktype',type,'l');
call symputx('liburi',uri,'l');
putlog (_all_)(=);
run;
%if &checktype ne SASLibrary %then %do;
%put &sysmacroname: Library (&name) was not found, and so will not be deleted;
%return;
%end;
%local fname1 fname2;
%let fname1=%mf_getuniquefileref();
%let fname2=%mf_getuniquefileref();
filename &fname1 temp lrecl=10000;
filename &fname2 temp lrecl=10000;
data _null_ ;
file &fname1 ;
put "<DeleteMetadata><Metadata><SASLibrary Id='&liburi'/>";
put "</Metadata><NS>SAS</NS><Flags>268436480</Flags><Options/>";
put "</DeleteMetadata>";
run ;
proc metadata in=&fname1 out=&fname2 verbose;run;
/* list the result */
data _null_;infile &fname2; input; list; run;
filename &fname1 clear;
filename &fname2 clear;
/**
* Check deletion
*/
%local isgone;
data _null_;
length type uri $256;
rc=metadata_resolve("omsobj:SASLibrary?@Id='&liburi'",type,uri);
call symputx('isgone',type,'l');
run;
%mp_abort(iftrue=(&isgone = SASLibrary)
,mac=&sysmacroname
,msg=%str(Library (&name) NOT deleted)
)
%put &sysmacroname: Library &name (&liburi) was successfully deleted;
%mend;

View File

@@ -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

View File

@@ -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

View File

@@ -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 ('&#x0a;') rec='0A'x; when ('&#x0a;') rec='0A'x;
when ('&#x0d;') rec='0D'x; when ('&#x0d;') rec='0D'x;
when ('&#36;' ) rec='$' ; when ('&#36;' ) rec='$' ;
when ('&#x09;') 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));

View File

@@ -0,0 +1,95 @@
/**
@file
@brief Returns all direct child members of a particular folder
@details Displays the children for a particular folder, in a similar fashion
to the viya counterpart (mv_getfoldermembers.sas)
Usage:
%mm_getfoldermembers(root=/, outds=rootfolders)
%mm_getfoldermembers(root=/User Folders/&sysuserid, outds=usercontent)
@param [in] root= the parent folder under which to return all contents
@param [out] outds= the dataset to create that contains the list of directories
@param [in] mDebug= set to 1 to show debug messages in the log
<h4> Data Outputs </h4>
Example for `root=/`:
|metauri $17|metaname $256|metatype $32|
|---|---|---|
|A5XLSNXI.AA000001|Products |Folder|
|A5XLSNXI.AA000002|Shared Data |Folder|
|A5XLSNXI.AA000003|User Folders |Folder|
|A5XLSNXI.AA000004|System |Folder|
|A5XLSNXI.AA00003K|30.SASApps |Folder|
|A5XLSNXI.AA00006A|Public|Folder|
<h4> SAS Macros </h4>
@li mm_getfoldertree.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@version 9.4
@author Allan Bowe
**/
%macro mm_getfoldermembers(
root=
,outds=work.mm_getfoldertree
)/*/STORE SOURCE*/;
%if "&root" = "/" %then %do;
%local fname1 fname2 fname3;
%let fname1=%mf_getuniquefileref();
%let fname2=%mf_getuniquefileref();
%let fname3=%mf_getuniquefileref();
data _null_ ;
file &fname1 ;
put '<GetMetadataObjects>' ;
put '<Reposid>$METAREPOSITORY</Reposid>' ;
put '<Type>Tree</Type>' ;
put '<NS>SAS</NS>' ;
put '<Flags>388</Flags>' ;
put '<Options>' ;
put '<XMLSelect search="Tree[SoftwareComponents/SoftwareComponent'@;
put '[@Name=''BIP Service'']]"/>';
put '</Options>' ;
put '</GetMetadataObjects>' ;
run ;
proc metadata in=&fname1 out=&fname2 verbose;run;
/* create an XML map to read the response */
data _null_;
file &fname3;
put '<SXLEMAP version="1.2" name="SASFolders">';
put '<TABLE name="SASFolders">';
put '<TABLE-PATH syntax="XPath">//Objects/Tree</TABLE-PATH>';
put '<COLUMN name="metauri">><LENGTH>17</LENGTH>';
put '<PATH syntax="XPath">//Objects/Tree/@Id</PATH></COLUMN>';
put '<COLUMN name="metaname"><LENGTH>256</LENGTH>>';
put '<PATH syntax="XPath">//Objects/Tree/@Name</PATH></COLUMN>';
put '</TABLE></SXLEMAP>';
run;
%local libref1;
%let libref1=%mf_getuniquelibref();
libname &libref1 xml xmlfileref=&fname2 xmlmap=&fname3;
data &outds;
length metatype $32;
retain metatype 'Folder';
set &libref1..sasfolders;
run;
%end;
%else %do;
%mm_getfoldertree(root=&root, outds=&outds,depth=1)
data &outds;
set &outds(rename=(name=metaname publictype=metatype));
keep metaname metauri metatype;
run;
%end;
%mend;

View File

@@ -1,20 +1,22 @@
/** /**
@file mm_getfoldertree.sas @file
@brief Returns all folders / subfolder content for a particular root @brief Returns all folders / subfolder content for a particular root
@details Shows all members and SubTrees recursively for a particular root. @details Shows all members and SubTrees recursively for a particular root.
Note - for big sites, this returns a lot of data! So you may wish to reduce Note - for big sites, this returns a lot of data! So you may wish to reduce
the logging to speed up the process (see example below) the logging to speed up the process (see example below), OR - use mm_tree.sas
which uses proc metadata and is far more efficient.
Usage: Usage:
options ps=max nonotes nosource; options ps=max nonotes nosource;
%mm_getfoldertree(root=/My/Meta/Path, outds=iwantthisdataset) %mm_getfoldertree(root=/My/Meta/Path, outds=iwantthisdataset)
options notes source; options notes source;
@param root= the parent folder under which to return all contents @param [in] root= the parent folder under which to return all contents
@param outds= the dataset to create that contains the list of directories @param [out] outds= the dataset to create that contains the list of directories
@param mDebug= set to 1 to show debug messages in the log @param [in] 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

View File

@@ -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

View File

@@ -7,7 +7,7 @@
%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

View File

@@ -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

View File

@@ -123,6 +123,7 @@ data _null_;
when ('&#x0a;') rec='0A'x; when ('&#x0a;') rec='0A'x;
when ('&#x0d;') rec='0D'x; when ('&#x0d;') rec='0D'x;
when ('&#36;' ) rec='$' ; when ('&#36;' ) rec='$' ;
when ('&#x09;') 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));

View File

@@ -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
View 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;

View File

@@ -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 ('&#x0a;') rec='0A'x; when ('&#x0a;') rec='0A'x;
when ('&#x0d;') rec='0D'x; when ('&#x0d;') rec='0D'x;
when ('&#36;' ) rec='$' ; when ('&#36;' ) rec='$' ;
when ('&#x09;') 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));

View File

@@ -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

View File

@@ -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

View File

@@ -1,5 +1,5 @@
/** /**
@file mm_updatestpservertype.sas @file
@brief Updates a type 2 stored process to run on STP or WKS context @brief Updates a type 2 stored process to run on STP or WKS context
@details Only works on Type 2 (9.3 compatible) STPs @details Only works on Type 2 (9.3 compatible) STPs
@@ -8,7 +8,6 @@
%mm_updatestpservertype(target=/some/meta/path/myStoredProcess %mm_updatestpservertype(target=/some/meta/path/myStoredProcess
,type=WKS) ,type=WKS)
<h4> Dependencies </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

View File

@@ -9,28 +9,37 @@
%mm_updatestpsourcecode(stp=/my/metadata/path/mystpname %mm_updatestpsourcecode(stp=/my/metadata/path/mystpname
,stpcode="/file/system/source.sas") ,stpcode="/file/system/source.sas")
@param [in] stp= the BIP Tree folder path plus Stored Process Name
@param stp= the BIP Tree folder path plus Stored Process Name @param [in] stpcode= the source file (or fileref) containing the SAS code to load
@param stpcode= the source file (or fileref) containing the SAS code to load
into the stp. For multiple files, they should simply be concatenated first. into the stp. For multiple files, they should simply be concatenated first.
@param minify= set to YES in order to strip comments, blank lines, and CRLFs. @param [in] minify= set to YES in order to strip comments, blank lines, and CRLFs.
@param frefin= change default inref if it clashes with an existing one @param frefin= deprecated - a unique fileref is now always used
@param frefout= change default outref if it clashes with an existing one @param frefout= deprecated - a unique fileref is now always used
@param mDebug= set to 1 to show debug messages in the log @param mDebug= set to 1 to show debug messages in the log
@version 9.3 @version 9.3
@author Allan Bowe @author Allan Bowe
<h4> SAS Macros </h4>
@li mf_getuniquefileref.sas
**/ **/
%macro mm_updatestpsourcecode(stp= %macro mm_updatestpsourcecode(stp=
,stpcode= ,stpcode=
,minify=NO ,minify=NO
,mdebug=0
/* deprecated */
,frefin=inmeta ,frefin=inmeta
,frefout=outmeta ,frefout=outmeta
,mdebug=0
); );
%if &frefin ne inmeta or &frefout ne outmeta %then %do;
%put %str(WARN)ING: the frefin and frefout parameters will be deprecated in
an upcoming release.;
%end;
/* first, check if STP exists */ /* first, check if STP exists */
%local tsuri; %local tsuri;
%let tsuri=stopifempty ; %let tsuri=stopifempty ;
@@ -68,7 +77,9 @@ run;
%return; %return;
%end; %end;
filename &frefin temp lrecl=32767; %local frefin frefout;
%let frefin=%mf_getuniquefileref();
%let frefout=%mf_getuniquefileref();
/* write header XML */ /* write header XML */
data _null_; data _null_;
@@ -81,7 +92,7 @@ run;
/* write contents */ /* write contents */
%if %length(&stpcode)>2 %then %do; %if %length(&stpcode)>2 %then %do;
data _null_; data _null_;
file &frefin mod; file &frefin lrecl=32767 mod;
infile &stpcode lrecl=32767; infile &stpcode lrecl=32767;
length outstr $32767; length outstr $32767;
input outstr ; input outstr ;
@@ -110,9 +121,6 @@ data _null_;
</UpdateMetadata>"; </UpdateMetadata>";
run; run;
filename &frefout temp;
proc metadata in= &frefin out=&frefout; proc metadata in= &frefin out=&frefout;
run; run;
@@ -124,5 +132,9 @@ run;
put _infile_; put _infile_;
run; run;
%end; %end;
%else %do;
filename &frefin clear;
filename &frefout clear;
%end;
%mend; %mend;

View File

@@ -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

View File

@@ -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

35
package-lock.json generated
View File

@@ -1,4 +1,35 @@
{ {
"name": "macrocore", "name": "@sasjs/core",
"lockfileVersion": 1 "version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@sasjs/core",
"version": "1.0.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"node-git-hooks": "^1.0.5"
}
},
"node_modules/node-git-hooks": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/node-git-hooks/-/node-git-hooks-1.0.5.tgz",
"integrity": "sha512-05rULsJy8z2OvXTQFZv9fN20uo186EYgJYQjkL1OjnXj53QivOAGUzZilZ/MX8OmPRaN+deJBtlvMRydpdfnqA==",
"bin": {
"node-git-hooks": "bin/install.js"
},
"engines": {
"node": ">=4.0.0"
}
}
},
"dependencies": {
"node-git-hooks": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/node-git-hooks/-/node-git-hooks-1.0.5.tgz",
"integrity": "sha512-05rULsJy8z2OvXTQFZv9fN20uo186EYgJYQjkL1OjnXj53QivOAGUzZilZ/MX8OmPRaN+deJBtlvMRydpdfnqA=="
}
}
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@sasjs/core", "name": "@sasjs/core",
"description": "SAS Macro Library for Application Development", "description": "Production Ready Macros for SAS Application Developers",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
"SAS", "SAS",
@@ -10,13 +10,28 @@
"author": "Allan Bowe <support@macropeople.com>", "author": "Allan Bowe <support@macropeople.com>",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/sasjs/core" "url": "git+https://github.com/sasjs/core.git"
}, },
"release": { "release": {
"branches": ["main"] "branches": [
"main"
]
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
"devDependencies": {} "bugs": {
"url": "https://github.com/sasjs/core/issues"
},
"homepage": "https://github.com/sasjs/core#readme",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"docs": "sasjs doc && ./sasjs/utils/build.sh",
"postinstall": "node-git-hooks"
},
"dependencies": {
"node-git-hooks": "^1.0.5"
}
} }

View File

@@ -1,5 +1,5 @@
ALPHABETICAL_INDEX = NO ALPHABETICAL_INDEX = NO
DISABLE_INDEX = YES DISABLE_INDEX = NO
ENABLE_PREPROCESSING = NO ENABLE_PREPROCESSING = NO
EXTENSION_MAPPING = sas=Java ddl=Java EXTENSION_MAPPING = sas=Java ddl=Java
EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_CLASSES = NO
@@ -13,17 +13,21 @@ HIDE_IN_BODY_DOCS = YES
HIDE_SCOPE_NAMES = YES HIDE_SCOPE_NAMES = YES
HIDE_UNDOC_CLASSES = YES HIDE_UNDOC_CLASSES = YES
HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_MEMBERS = YES
HTML_OUTPUT = doxy HTML_OUTPUT = $(DOXY_HTML_OUTPUT)
HTML_HEADER = ./doxy/new_header.html HTML_HEADER = $(HTML_HEADER)
HTML_FOOTER = ./doxy/new_footer.html HTML_EXTRA_FILES = $(HTML_EXTRA_FILES)
HTML_EXTRA_STYLESHEET = ./doxy/new_stylesheet.css HTML_FOOTER = $(HTML_FOOTER)
HTML_EXTRA_STYLESHEET = $(HTML_EXTRA_STYLESHEET)
INHERIT_DOCS = NO INHERIT_DOCS = NO
INLINE_INFO = NO INLINE_INFO = NO
INPUT = base meta metax viya INPUT = $(DOXY_INPUT)
LAYOUT_FILE = ./doxy/DoxygenLayout.xml INPUT += main.dox
LAYOUT_FILE = $(LAYOUT_FILE)
USE_MDFILE_AS_MAINPAGE = README.md
MAX_INITIALIZER_LINES = 0 MAX_INITIALIZER_LINES = 0
PROJECT_NAME = Macro Core PROJECT_NAME = $(PROJECT_NAME)
PROJECT_LOGO = doxy/Macro_core_website_1.png PROJECT_LOGO = $(PROJECT_LOGO)
PROJECT_BRIEF = $(PROJECT_BRIEF)
RECURSIVE = YES RECURSIVE = YES
REPEAT_BRIEF = NO REPEAT_BRIEF = NO
SHOW_NAMESPACES = NO SHOW_NAMESPACES = NO

View File

@@ -2,7 +2,7 @@
<!-- Generated by doxygen 1.8.14 --> <!-- Generated by doxygen 1.8.14 -->
<!-- Navigation index tabs for HTML output --> <!-- Navigation index tabs for HTML output -->
<navindex> <navindex>
<tab type="mainpage" visible="no" title=""/> <tab type="mainpage" visible="yes" title="Home"/>
<tab type="pages" visible="no" title="" intro=""/> <tab type="pages" visible="no" title="" intro=""/>
<tab type="modules" visible="no" title="" intro=""/> <tab type="modules" visible="no" title="" intro=""/>
<tab type="namespaces" visible="no" title=""> <tab type="namespaces" visible="no" title="">
@@ -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>

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

26
sasjs/doxy/doxygen.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More