diff --git a/jenner-check/.gitignore b/jenner-check/.gitignore new file mode 100644 index 0000000..75fb4a3 --- /dev/null +++ b/jenner-check/.gitignore @@ -0,0 +1,2 @@ +*_response.json +response.json diff --git a/jenner-check/README.md b/jenner-check/README.md new file mode 100644 index 0000000..58459d6 --- /dev/null +++ b/jenner-check/README.md @@ -0,0 +1,76 @@ +# jenner-check: compatibility test bundles + +Each `tNNN_/` subdirectory is a self-contained test bundle built from SAS +code that already lives in this repository. A bundle pins the output of a +captured passing run so the same script can be re-run later and compared +against that snapshot. + +This directory is fully self-contained: nothing outside `jenner-check/` is +referenced or modified, nothing runs on merge or checkout, and deleting the +directory removes every trace of it. + +## What the runner sends — read before running + +`run_jenner.sh` uploads only a bundle's SAS **source text** — `autoexec.sas` +plus `script.sas`, and nothing else — over HTTPS to `api.jenneranalytics.com`, +where it runs and returns the log and listing. It does not read or upload any +data files, so anything sitting next to a script stays on your machine; the +only thing transmitted is the code you run, as when pasting a snippet into any +hosted tool. Nothing is sent unless you run a command yourself. To point the +runner at a different endpoint, set `JENNER_HOST`. + +## What's in here + +``` +jenner-check/ +├── README.md # this file +├── run_jenner.sh # runner (bash + curl; python3 used if present) +└── tNNN_/ + ├── script.sas # the SAS under test (derived from this repo) + ├── autoexec.sas # run options prepended to script.sas at run time + ├── expected.json # fields pinned from the captured passing run + ├── meta.json # provenance: source file, blob sha, commit + └── expected/ # human-readable snapshot of that run + ├── log.txt + └── output.txt +``` + +`meta.json` records the source path, blob sha, and commit of the script each +bundle was built from, so you can verify the copy matches your code. + +## How to run + +From inside this directory: + +```bash +./run_jenner.sh --list # show the bundles in this directory +./run_jenner.sh --all # run every bundle, verify pinned fields +./run_jenner.sh tNNN_ # run just one (use a name from --list) +``` + +For each bundle the runner submits the SAS, writes the JSON response to +`_response.json`, and compares the response against the bundle's +`expected.json` — status, exit code, and the pinned log lines. A bundle +passes only when every pinned field matches, so an `N pass, 0 fail` summary +from `--all` means the captured results still hold. + +You can also compare a saved response offline, with no network call: + +```bash +./run_jenner.sh --compare tNNN__response.json tNNN_/expected.json +``` + +Requirements: `bash` 4+ and `curl` (both ship with mainstream Linux and +macOS); `python3` for the pinned-field comparison. On Windows, run it under +WSL. + +## Removing this directory + +`git rm -r jenner-check/` (or just delete the folder). Nothing else in the +repository references it. + +--- + +`run_jenner.sh` and this README are provided under the MIT license. Each +bundle's `script.sas` is a copy of code that already lives in this repository +and remains under this repository's own license — nothing here relicenses it. diff --git a/jenner-check/run_jenner.sh b/jenner-check/run_jenner.sh new file mode 100755 index 0000000..ad7825a --- /dev/null +++ b/jenner-check/run_jenner.sh @@ -0,0 +1,357 @@ +#!/usr/bin/env bash +# run_jenner.sh - mac/linux runner for Jenner compatibility checks. +# +# Quick start: +# cd jenner-check/ +# ./run_jenner.sh # lists bundles in the current dir +# ./run_jenner.sh t001_something # run that one +# ./run_jenner.sh --all # run every bundle in the current dir +# +# Usage: ./run_jenner.sh [bundle-dir | script.sas | --all | --list] [response.json] +# ./run_jenner.sh --compare RESPONSE.json EXPECTED.json +# +# (no arg) If the current directory has tNNN_* bundles, list them +# with a copy-paste command. Otherwise show this help. +# +# --all Run every tNNN_* bundle in the current directory in +# sequence, print a pass/fail summary. +# +# --list, -l List the bundles visible in the current directory and +# exit without running anything. +# +# --compare Purely local, no network: check RESPONSE.json against the +# pinned EXPECTED.json and print one line per check. Only +# keys present in EXPECTED.json are checked (keys starting +# with "_" are provenance and ignored): status / exit_code +# must match exactly, every log_contains entry must appear +# in the response log, no log_does_not_contain entry may +# appear, output_contains entries must appear in the output, +# and pinned diagnostics lists must match exactly. Unknown +# keys print a warning and do not fail. +# Exit 0 all-match, 1 any mismatch, 2 usage/unreadable file/ +# invalid JSON, 5 python3 unavailable. +# +# bundle-dir A directory containing script.sas and (optionally) +# autoexec.sas. The two are concatenated (autoexec first, +# then a blank line, then script) and submitted together. +# This is the normal case. +# +# script.sas A single .sas file. Submitted as-is — no autoexec. +# +# The API response is written to (or response.json in +# the current directory if omitted) and the most useful fields are also +# printed to stdout for a quick sanity check. +# +# What "pass" means: a bundle run passes only if the POST returns HTTP 200 +# AND — when the bundle carries an expected.json and python3 is available — +# the fresh response matches every pinned field (the --compare semantics +# above). Without python3 the pinned comparison is skipped with a notice +# and the run falls back to the old HTTP/status-only behavior. +# +# Requires: bash 4+, curl. Both ship with every mainstream Linux distro +# and macOS 12+. python3 (present on virtually every modern system) is +# optional but needed for the pinned-result comparison and the pretty +# summary. Windows: use run_jenner.bat (single-file mode) or WSL. +# +# IMPORTANT: execute this script, don't source it. Running with `. ./...` +# or `source ./...` will short-circuit error handling and can close your +# terminal if an error path fires. + +# --- refuse to be sourced ------------------------------------------------ +# `return` only works inside a sourced script. If we ARE sourced, print a +# message and return 1 so we don't kill the parent shell with exit. If +# we're running directly, (return 0) fails and we fall through. +(return 0 2>/dev/null) && { + printf 'run_jenner.sh: execute this script, do not source it.\n ./run_jenner.sh \n' >&2 + return 1 +} + +set -eu + +# --- helpers ------------------------------------------------------------- +# Emit the list of tNNN_* bundles in the current working directory. A +# "bundle" is a directory matching t[0-9]*_* whose name contains a +# script.sas file. Writes one path per line (no prefix); empty output +# if nothing found. +list_bundles_here() { + local d + for d in ./t[0-9]*_*/ ; do + [[ -d "$d" && -f "$d/script.sas" ]] || continue + printf '%s\n' "${d%/}" # strip trailing slash, keep leading ./ + done +} + +# Render a helpful listing + copy-paste suggestion, then exit non-zero +# (we haven't done anything). Used when the user runs with no args. +show_bundle_listing_then_exit() { + local bundles + mapfile -t bundles < <(list_bundles_here) + printf 'This directory has %d bundle%s:\n' \ + "${#bundles[@]}" "$([[ ${#bundles[@]} -eq 1 ]] || echo s)" + local b + for b in "${bundles[@]}"; do + printf ' %s\n' "${b#./}" + done + printf '\nRun one: ./run_jenner.sh %s\n' "${bundles[0]#./}" + printf 'Run them all: ./run_jenner.sh --all\n' + printf 'Just list: ./run_jenner.sh --list\n' + exit 2 +} + +# Show the usage block when we have nothing better to offer. +show_usage_then_exit() { + local status=${1:-2} + { + printf 'Usage: %s [bundle-dir | script.sas | --all | --list] [response.json]\n' "$(basename "$0")" + printf ' %s --compare RESPONSE.json EXPECTED.json\n\n' "$(basename "$0")" + printf 'Examples:\n' + printf ' %s t001_my_bundle # run one bundle\n' "$(basename "$0")" + printf ' %s --all # run every tNNN_* bundle in this dir\n' "$(basename "$0")" + printf ' %s path/to/script.sas # run a single file, no autoexec\n' "$(basename "$0")" + printf ' %s --compare r.json e.json # check a response against pinned fields\n' "$(basename "$0")" + } >&2 + exit "$status" +} + +# Compare a response.json against a pinned expected.json. Purely local — +# no network. Only keys present in expected.json are checked; keys whose +# name starts with "_" are capture provenance and are ignored. Prints one +# line per check (ok/FAIL/warn). Return codes: 0 all checks match, +# 1 any mismatch, 2 unreadable file or invalid JSON, 5 python3 missing. +compare_response_to_expected() { + local resp_file=$1 expected_file=$2 + if ! command -v python3 >/dev/null 2>&1; then + printf 'error: the pinned comparison needs python3, which was not found\n' >&2 + return 5 + fi + python3 - "$resp_file" "$expected_file" <<'PY' +import json, sys + +def load(path, label): + try: + with open(path, encoding="utf-8") as f: + return json.load(f) + except (OSError, ValueError) as e: + print(f"compare: cannot read {label} file {path}: {e}", file=sys.stderr) + sys.exit(2) + +resp = load(sys.argv[1], "response") +exp = load(sys.argv[2], "expected") +if not isinstance(resp, dict) or not isinstance(exp, dict): + print("compare: both files must contain a JSON object", file=sys.stderr) + sys.exit(2) + +failures = 0 + +def ok(msg): + print(f" ok {msg}") + +def bad(msg): + global failures + failures += 1 + print(f" FAIL {msg}") + +def as_list(v): + return v if isinstance(v, list) else [v] + +log = resp.get("log") or "" +output = resp.get("output") or "" + +for key, want in exp.items(): + if key.startswith("_"): + continue # provenance (_captured_at, ...), not a check + if key in ("status", "exit_code"): + got = resp.get(key) + if got == want: + ok(f"{key} == {want!r}") + else: + bad(f"{key}: expected {want!r}, got {got!r}") + elif key == "log_contains": + for s in as_list(want): + if s in log: + ok(f"log contains {s!r}") + else: + bad(f"log_contains: log is missing {s!r}") + elif key == "log_does_not_contain": + for s in as_list(want): + if s not in log: + ok(f"log does not contain {s!r}") + else: + bad(f"log_does_not_contain: log unexpectedly contains {s!r}") + elif key == "output_contains": + for s in as_list(want): + if s in output: + ok(f"output contains {s!r}") + else: + bad(f"output_contains: output is missing {s!r}") + elif key == "diagnostics": + diag = resp.get("diagnostics") or {} + for dk, dv in (want or {}).items(): + if dk.startswith("_"): + continue + got = diag.get(dk) + if got == dv: + ok(f"diagnostics.{dk} == {dv!r}") + else: + bad(f"diagnostics.{dk}: expected {dv!r}, got {got!r}") + else: + print(f" warn unknown expected.json key {key!r} — not checked") + +if failures: + print(f"pinned comparison: {failures} check(s) FAILED") + sys.exit(1) +print("pinned comparison: all pinned checks passed") +PY +} + +# --- arg parsing --------------------------------------------------------- +if [[ $# -lt 1 ]]; then + # No args: if the cwd contains bundles, list them; otherwise show help. + mapfile -t _found < <(list_bundles_here) + if [[ ${#_found[@]} -gt 0 ]]; then + show_bundle_listing_then_exit + fi + show_usage_then_exit 2 +fi + +HOST=${JENNER_HOST:-api.jenneranalytics.com} + +case "$1" in + -h|--help) + show_usage_then_exit 0 + ;; + -l|--list) + mapfile -t _found < <(list_bundles_here) + if [[ ${#_found[@]} -eq 0 ]]; then + printf 'No tNNN_* bundles found in %s\n' "$(pwd)" + exit 0 + fi + printf 'Bundles in %s:\n' "$(pwd)" + for b in "${_found[@]}"; do + printf ' %s\n' "${b#./}" + done + exit 0 + ;; + --compare) + # Purely local: diff a response.json against a pinned expected.json. + if [[ $# -ne 3 ]]; then + printf 'usage: %s --compare RESPONSE.json EXPECTED.json\n' "$(basename "$0")" >&2 + exit 2 + fi + _rc=0 + compare_response_to_expected "$2" "$3" || _rc=$? + exit "$_rc" + ;; + --all) + mapfile -t _found < <(list_bundles_here) + if [[ ${#_found[@]} -eq 0 ]]; then + printf 'No tNNN_* bundles found in %s\n' "$(pwd)" >&2 + exit 3 + fi + _pass=0; _fail=0 + for b in "${_found[@]}"; do + printf '\n── %s ──\n' "${b#./}" + if "$0" "$b" "${b#./}_response.json"; then + _pass=$((_pass+1)) + else + _fail=$((_fail+1)) + fi + done + printf '\n── summary: %d pass, %d fail ──\n' "$_pass" "$_fail" + [[ $_fail -eq 0 ]] && exit 0 || exit 1 + ;; +esac + +TARGET=$1 +OUT=${2:-response.json} + +# --- assemble the submission body --------------------------------------- +# If TARGET is a directory, treat it as a bundle. If it's a file, submit +# it directly. +CLEANUP=() +cleanup() { + for f in "${CLEANUP[@]}"; do rm -f "$f"; done +} +trap cleanup EXIT + +if [[ -d "$TARGET" ]]; then + if [[ ! -f "$TARGET/script.sas" ]]; then + printf 'error: %s is a directory but has no script.sas\n' "$TARGET" >&2 + exit 3 + fi + SUBMIT=$(mktemp -t jc_submit.XXXXXX.sas) + CLEANUP+=("$SUBMIT") + if [[ -f "$TARGET/autoexec.sas" ]]; then + cat "$TARGET/autoexec.sas" > "$SUBMIT" + printf '\n' >> "$SUBMIT" + fi + cat "$TARGET/script.sas" >> "$SUBMIT" + printf 'Submitting bundle: %s\n' "$TARGET" + if [[ -f "$TARGET/autoexec.sas" ]]; then + printf ' autoexec.sas (%d bytes) + script.sas (%d bytes)\n' \ + "$(wc -c < "$TARGET/autoexec.sas")" "$(wc -c < "$TARGET/script.sas")" + else + printf ' script.sas (%d bytes), no autoexec\n' "$(wc -c < "$TARGET/script.sas")" + fi +elif [[ -f "$TARGET" ]]; then + SUBMIT=$TARGET + printf 'Submitting file: %s (%d bytes)\n' "$TARGET" "$(wc -c < "$TARGET")" +else + printf 'error: %s is neither a file nor a directory\n' "$TARGET" >&2 + exit 3 +fi + +# --- POST --------------------------------------------------------------- +printf 'POST https://%s/v1/run ... ' "$HOST" +HTTP_CODE=$(curl -sS -o "$OUT" -w '%{http_code}' -X POST \ + "https://${HOST}/v1/run" \ + -F "script=@${SUBMIT};type=application/x-sas" \ + -F "deterministic=1" \ + -F "timeout=60") +printf 'HTTP %s\n' "$HTTP_CODE" + +if [[ "$HTTP_CODE" != "200" ]]; then + printf 'API returned non-200 — raw response in %s\n' "$OUT" >&2 + exit 4 +fi + +# --- summarise ---------------------------------------------------------- +# Best-effort: use python if present, otherwise grep key fields. +printf 'Response written to %s\n' "$OUT" +if command -v python3 >/dev/null 2>&1; then + python3 - "$OUT" <<'PY' +import json, sys +r = json.load(open(sys.argv[1])) +print(f" status : {r.get('status')}") +print(f" exit_code : {r.get('exit_code')}") +print(f" duration_ms: {r.get('duration_ms')}") +print(f" run_id : {r.get('run_id')}") +print(f" jenner_ver : {r.get('jenner_version')}") +log = r.get('log', '') +if log: + print(' log (first 10 lines):') + for line in log.splitlines()[:10]: + print(f' {line}') +PY +else + printf ' (install python3 for a pretty summary; raw JSON in %s)\n' "$OUT" +fi + +# --- pinned-result comparison --------------------------------------------- +# A bundle that ships an expected.json only passes if the fresh response +# matches every pinned field — HTTP 200 alone is not a pass. Without +# python3 we can't parse JSON portably, so fall back to the old behavior +# and say so. +if [[ -d "$TARGET" && -f "$TARGET/expected.json" ]]; then + if command -v python3 >/dev/null 2>&1; then + printf 'Comparing response against pinned %s/expected.json\n' "${TARGET%/}" + _rc=0 + compare_response_to_expected "$OUT" "$TARGET/expected.json" || _rc=$? + if [[ $_rc -ne 0 ]]; then + printf 'Bundle FAILED the pinned comparison (see lines above)\n' >&2 + exit "$_rc" + fi + else + printf 'pinned comparison skipped (python3 not found) — HTTP/status only\n' + fi +fi diff --git a/jenner-check/t001_osha_accident/autoexec.sas b/jenner-check/t001_osha_accident/autoexec.sas new file mode 100644 index 0000000..719f93f --- /dev/null +++ b/jenner-check/t001_osha_accident/autoexec.sas @@ -0,0 +1,2 @@ +/* cap input rows for the captured run */ +options obs=100; diff --git a/jenner-check/t001_osha_accident/expected.json b/jenner-check/t001_osha_accident/expected.json new file mode 100644 index 0000000..7e3d820 --- /dev/null +++ b/jenner-check/t001_osha_accident/expected.json @@ -0,0 +1,18 @@ +{ + "_captured_at": "2026-08-19T05:46:41Z", + "_captured_run_id": "r_c70f30709ba047408e76f2ed1b155d3f", + "status": "ok", + "exit_code": 0, + "log_contains": [ + "NOTE: Wrote WORK.osha_accident (12 rows, 15 columns).", + "NOTE: PROC PRINT completed: 12 observations printed, 15 variables" + ], + "log_does_not_contain": [ + "ERROR:", + "[JENNER-ERROR" + ], + "diagnostics": { + "parse_warnings": [], + "runtime_warnings": [] + } +} diff --git a/jenner-check/t001_osha_accident/expected/log.txt b/jenner-check/t001_osha_accident/expected/log.txt new file mode 100644 index 0000000..d98d72d --- /dev/null +++ b/jenner-check/t001_osha_accident/expected/log.txt @@ -0,0 +1,15 @@ +NOTE: Option OBS changed to 100. +NOTE: DATA WORK.osha_accident + +NOTE: Processing inline DATALINES (12 lines) + +NOTE: Read 12 rows from DATALINES. +NOTE: Wrote WORK.osha_accident (12 rows, 15 columns). +NOTE: DATA elapsed: + wall 0.00 seconds + cpu 0.00 seconds +NOTE: PROC PRINT data=WORK.osha_accident + +NOTE: PROC PRINT completed: 12 observations printed, 15 variables +NOTE: PROC FREQ +NOTE: PROC FREQ statement used. diff --git a/jenner-check/t001_osha_accident/expected/output.txt b/jenner-check/t001_osha_accident/expected/output.txt new file mode 100644 index 0000000..11372ea --- /dev/null +++ b/jenner-check/t001_osha_accident/expected/output.txt @@ -0,0 +1,40 @@ + OSHA Accident import (Jenner compatibility bundle) + + Obs Summary NR Report ID Event Date Event Time Event Description Event Keyword Construction End Use Stories in Building Non Building Height Project Cost Project Type SIC List Fatality State Flag Abstract text + 1 100001 55001 01JAN15:08:15:00 815 Worker fell from scaffold fall scaffold 1 3 . 1 1 1521 N C Y + 2 100002 55002 03JAN15:13:40:00 1340 Struck by falling object struck-by object 1 1 . 2 2 1731 N T Y + 3 100003 55003 05JAN15:09:05:00 905 Employee caught in machine caught-in machine . . 1 1 3559 Y O Y + 4 100004 55004 07JAN15:16:20:00 1620 Electrical shock during repair electrical shock . . 1 1 1731 N N Y + 5 100005 55005 10JAN15:11:00:00 1100 Trench collapse burying worker trench collapse 1 . 12 1 1 1611 Y P Y + 6 100006 55006 12JAN15:07:45:00 745 Forklift overturned forklift overturn . . 2 1 4213 N I Y + 7 100007 55007 14JAN15:15:10:00 1510 Chemical exposure in tank chemical exposure . . 1 1 2911 N L Y + 8 100008 55008 16JAN15:10:30:00 1030 Fall from roof edge fall roof 1 2 . 1 1 1761 Y F Y + 9 100009 55009 18JAN15:14:55:00 1455 Crane load dropped on worker struck-by crane 1 . . 1 1 1794 N W Y + 10 100010 55010 20JAN15:08:00:00 800 Fire during welding operation fire welding . . 1 1 3441 N M Y + 11 100011 55011 22JAN15:12:15:00 1215 Amputation on press machine amputation press . . 1 2 3469 N I Y + 12 100012 55012 24JAN15:09:40:00 940 Slip on wet warehouse floor slip fall . . 2 1 4225 N G Y + + OSHA Accident: state_flag and fatality distribution + + The FREQ Procedure + + Cumulative Cumulative +State Flag Frequency Percent Frequency Percent +------------------------------------------------------------------- +C 1 8.33 1 8.33 +F 1 8.33 2 16.67 +G 1 8.33 3 25.00 +I 2 16.67 5 41.67 +L 1 8.33 6 50.00 +M 1 8.33 7 58.33 +N 1 8.33 8 66.67 +O 1 8.33 9 75.00 +P 1 8.33 10 83.33 +T 1 8.33 11 91.67 +W 1 8.33 12 100.00 + + Cumulative Cumulative +Fatality Frequency Percent Frequency Percent +----------------------------------------------------------------- +N 9 75.00 9 75.00 +Y 3 25.00 12 100.00 diff --git a/jenner-check/t001_osha_accident/meta.json b/jenner-check/t001_osha_accident/meta.json new file mode 100644 index 0000000..ca7a334 --- /dev/null +++ b/jenner-check/t001_osha_accident/meta.json @@ -0,0 +1,7 @@ +{ + "bundle": "t001_osha_accident", + "source_file": "SAS/osha_accident_sas.sas", + "source_blob_sha": "d86c61889b368574d8d4594821df62ef28ba2587", + "source_commit": "ddcc1c32fab05c755c42622b4c42dbe857a665e8", + "notes": "Imports the OSHA accident-summary layout (DATETIME18./ANYDTDTM19. event_date, 15 columns). Original reads osha_accident.csv via &location/INFILE; this bundle replaces that with 12 inline DATALINES rows (comma-delimited variant the script itself documents) and adds a PROC PRINT + PROC FREQ to exercise the imported data. All LENGTH/FORMAT/INFORMAT/LABEL/INPUT statements are unmodified from the source." +} diff --git a/jenner-check/t001_osha_accident/script.sas b/jenner-check/t001_osha_accident/script.sas new file mode 100644 index 0000000..17ee6c9 --- /dev/null +++ b/jenner-check/t001_osha_accident/script.sas @@ -0,0 +1,126 @@ +/* -------------------------------------------------------------------- + IMPORT OSHA ACCIDENT FILE INTO SAS DATASET + (Jenner compatibility bundle — adapted from SAS/osha_accident_sas.sas) + + Original input: osha_accident.csv, downloaded from + http://ogesdw.dol.gov/views/data_summary.php via the &location macro + variable and an INFILE statement. + + Adaptation for this bundle only: the INFILE/&location mechanism is + replaced with an inline DATALINES block of 12 synthetic rows shaped + to the same LENGTH/FORMAT/INFORMAT/INPUT layout as the original + script, using the comma-delimited variant the original script + documents as its non-Unix alternative (DLM=',' MISSOVER DSD). Every + LENGTH/FORMAT/INFORMAT/LABEL/INPUT statement below is copied + unmodified from the original file. + -------------------------------------------------------------------- */ + +DATA WORK.osha_accident; + LENGTH + summary_nr 8 + report_id 8 + event_date 8 + event_time 8 + event_desc $ 60 + event_keyword $ 200 + const_end_use $ 1 + build_stories 8 + nonbuild_ht 8 + project_cost $ 1 + project_type $ 1 + sic_list $ 40 + fatality $ 1 + state_flag $ 1 + abstract_text $ 1 ; + FORMAT + summary_nr BEST9. + report_id BEST7. + event_date DATETIME18. + event_time BEST8. + event_desc $CHAR60. + event_keyword $CHAR200. + const_end_use $CHAR1. + build_stories BEST4. + nonbuild_ht BEST4. + project_cost $CHAR1. + project_type $CHAR1. + sic_list $CHAR40. + fatality $CHAR1. + state_flag $CHAR1. + abstract_text $CHAR1. ; + INFORMAT + summary_nr BEST9. + report_id BEST7. + event_date DATETIME18. + event_time BEST8. + event_desc $CHAR60. + event_keyword $CHAR200. + const_end_use $CHAR1. + build_stories BEST4. + nonbuild_ht BEST4. + project_cost $CHAR1. + project_type $CHAR1. + sic_list $CHAR40. + fatality $CHAR1. + state_flag $CHAR1. + abstract_text $CHAR1. ; + LABEL + summary_nr='Summary NR' + report_id='Report ID' + event_date='Event Date' + event_time='Event Time' + event_desc='Event Description' + event_keyword='Event Keyword' + const_end_use='Construction End Use' + build_stories='Stories in Building' + nonbuild_ht='Non Building Height' + project_cost='Project Cost' + project_type='Project Type' + sic_list='SIC List' + fatality='Fatality' + state_flag='State Flag' + abstract_text='Abstract text'; + INFILE DATALINES + DLM=',' + MISSOVER + DSD ; + INPUT + summary_nr : ?? BEST9. + report_id : ?? BEST7. + event_date : ?? ANYDTDTM19. + event_time : ?? BEST8. + event_desc : $CHAR60. + event_keyword : $CHAR200. + const_end_use : $CHAR1. + build_stories : ?? BEST4. + nonbuild_ht : ?? BEST4. + project_cost : $CHAR1. + project_type : $CHAR1. + sic_list : $CHAR40. + fatality : $CHAR1. + state_flag : $CHAR1. + abstract_text : $CHAR1. ; +DATALINES; +100001,55001,01JAN2015:08:15:00,0815,Worker fell from scaffold,fall scaffold,1,3,,1,1,1521,N,C,Y +100002,55002,03JAN2015:13:40:00,1340,Struck by falling object,struck-by object,1,1,,2,2,1731,N,T,Y +100003,55003,05JAN2015:09:05:00,0905,Employee caught in machine,caught-in machine,,,,1,1,3559,Y,O,Y +100004,55004,07JAN2015:16:20:00,1620,Electrical shock during repair,electrical shock,,,,1,1,1731,N,N,Y +100005,55005,10JAN2015:11:00:00,1100,Trench collapse burying worker,trench collapse,1,,12,1,1,1611,Y,P,Y +100006,55006,12JAN2015:07:45:00,0745,Forklift overturned,forklift overturn,,,,2,1,4213,N,I,Y +100007,55007,14JAN2015:15:10:00,1510,Chemical exposure in tank,chemical exposure,,,,1,1,2911,N,L,Y +100008,55008,16JAN2015:10:30:00,1030,Fall from roof edge,fall roof,1,2,,1,1,1761,Y,F,Y +100009,55009,18JAN2015:14:55:00,1455,Crane load dropped on worker,struck-by crane,1,,,1,1,1794,N,W,Y +100010,55010,20JAN2015:08:00:00,0800,Fire during welding operation,fire welding,,,,1,1,3441,N,M,Y +100011,55011,22JAN2015:12:15:00,1215,Amputation on press machine,amputation press,,,,1,2,3469,N,I,Y +100012,55012,24JAN2015:09:40:00,0940,Slip on wet warehouse floor,slip fall,,,,2,1,4225,N,G,Y +; +RUN; + +PROC PRINT DATA=WORK.osha_accident LABEL; + TITLE "OSHA Accident import (Jenner compatibility bundle)"; +RUN; + +PROC FREQ DATA=WORK.osha_accident; + TABLES state_flag fatality; + TITLE "OSHA Accident: state_flag and fatality distribution"; +RUN; diff --git a/jenner-check/t002_osha_inspection/autoexec.sas b/jenner-check/t002_osha_inspection/autoexec.sas new file mode 100644 index 0000000..719f93f --- /dev/null +++ b/jenner-check/t002_osha_inspection/autoexec.sas @@ -0,0 +1,2 @@ +/* cap input rows for the captured run */ +options obs=100; diff --git a/jenner-check/t002_osha_inspection/expected.json b/jenner-check/t002_osha_inspection/expected.json new file mode 100644 index 0000000..f6ce920 --- /dev/null +++ b/jenner-check/t002_osha_inspection/expected.json @@ -0,0 +1,18 @@ +{ + "_captured_at": "2026-08-19T05:46:41Z", + "_captured_run_id": "r_7a5e46a4927d4a4bb2d857ef64a2afca", + "status": "ok", + "exit_code": 0, + "log_contains": [ + "NOTE: Wrote WORK.osha_inspection (10 rows, 46 columns).", + "NOTE: PROC PRINT completed: 10 observations printed, 7 variables" + ], + "log_does_not_contain": [ + "ERROR:", + "[JENNER-ERROR" + ], + "diagnostics": { + "parse_warnings": [], + "runtime_warnings": [] + } +} diff --git a/jenner-check/t002_osha_inspection/expected/log.txt b/jenner-check/t002_osha_inspection/expected/log.txt new file mode 100644 index 0000000..a929787 --- /dev/null +++ b/jenner-check/t002_osha_inspection/expected/log.txt @@ -0,0 +1,15 @@ +NOTE: Option OBS changed to 100. +NOTE: DATA WORK.osha_inspection + +NOTE: Processing inline DATALINES (10 lines) + +NOTE: Read 10 rows from DATALINES. +NOTE: Wrote WORK.osha_inspection (10 rows, 46 columns). +NOTE: DATA elapsed: + wall 0.00 seconds + cpu 0.00 seconds +NOTE: PROC PRINT data=WORK.osha_inspection + +NOTE: PROC PRINT completed: 10 observations printed, 7 variables +NOTE: PROC FREQ +NOTE: PROC FREQ statement used. diff --git a/jenner-check/t002_osha_inspection/expected/output.txt b/jenner-check/t002_osha_inspection/expected/output.txt new file mode 100644 index 0000000..30e6c39 --- /dev/null +++ b/jenner-check/t002_osha_inspection/expected/output.txt @@ -0,0 +1,29 @@ + OSHA Inspection import (Jenner compatibility bundle) + + Obs Activity NR Establishment Name Site City Site State Inspection Type Open Date Case Closed Date + 1 900001 Acme Steel Fabrication Springfield IL A 2015-03-02 2015-03-25 + 2 900002 Lonestar Roofing Co Austin TX B 2015-04-11 2015-05-02 + 3 900003 Buckeye Chemical Plant Columbus OH A 2015-05-19 2015-06-15 + 4 900004 Empire Electrical Contractors Albany NY A 2015-06-08 2015-06-28 + 5 900005 Keystone Trenching LLC Pittsburgh PA A 2015-07-14 2015-08-05 + 6 900006 Prairie Forklift Services Peoria IL B 2015-08-22 2015-09-08 + 7 900007 Bayou Petrochemical Works Baton Rouge LA A 2015-09-30 2015-10-25 + 8 900008 Sunshine Roof & Deck Orlando FL B 2015-10-12 2015-11-02 + 9 900009 Cascade Crane & Rigging Seattle WA A 2015-11-05 2015-11-24 + 10 900010 Great Lakes Welding Inc Detroit MI B 2015-12-01 2015-12-19 + + OSHA Inspection: insp_type and owner_type distribution + + The FREQ Procedure + + Cumulative Cumulative +Inspection Type Frequency Percent Frequency Percent +------------------------------------------------------------------------ +A 6 60.00 6 60.00 +B 4 40.00 10 100.00 + + Cumulative Cumulative +Owner Type Frequency Percent Frequency Percent +------------------------------------------------------------------- +A 2 20.00 2 20.00 +B 8 80.00 10 100.00 diff --git a/jenner-check/t002_osha_inspection/meta.json b/jenner-check/t002_osha_inspection/meta.json new file mode 100644 index 0000000..1897192 --- /dev/null +++ b/jenner-check/t002_osha_inspection/meta.json @@ -0,0 +1,7 @@ +{ + "bundle": "t002_osha_inspection", + "source_file": "SAS/osha_inspection_sas.sas", + "source_blob_sha": "e148ceb687ae50dbaa42d6aa0190cb1e688cefbb", + "source_commit": "ddcc1c32fab05c755c42622b4c42dbe857a665e8", + "notes": "Imports the OSHA establishment-inspection layout, the widest of the ten templates (46 columns, YYMMDD10. dates for open/close/case-mod). Original reads osha_inspection.csv via &location/INFILE; this bundle replaces that with 10 inline DATALINES rows for synthetic establishments (comma-delimited variant the script documents) and adds a PROC PRINT + PROC FREQ. All LENGTH/FORMAT/INFORMAT/LABEL/INPUT statements are unmodified from the source." +} diff --git a/jenner-check/t002_osha_inspection/script.sas b/jenner-check/t002_osha_inspection/script.sas new file mode 100644 index 0000000..e29d532 --- /dev/null +++ b/jenner-check/t002_osha_inspection/script.sas @@ -0,0 +1,269 @@ +/* -------------------------------------------------------------------- + IMPORT OSHA INSPECTION FILE INTO SAS DATASET + (Jenner compatibility bundle — adapted from SAS/osha_inspection_sas.sas) + + Original input: osha_inspection.csv, downloaded from + http://ogesdw.dol.gov/views/data_summary.php via the &location macro + variable and an INFILE statement. + + Adaptation for this bundle only: the INFILE/&location mechanism is + replaced with an inline DATALINES block of 10 synthetic establishment + inspection rows shaped to the same LENGTH/FORMAT/INFORMAT/INPUT layout + as the original script, using the comma-delimited variant the original + script documents as its non-Unix alternative (DLM=',' MISSOVER DSD). + Every LENGTH/FORMAT/INFORMAT/LABEL/INPUT statement below is copied + unmodified from the original file. + -------------------------------------------------------------------- */ + +DATA WORK.osha_inspection; + LENGTH + activity_nr 8 + reporting_id 8 + state_flag $ 1 + estab_name $ 100 + site_address $ 142 + site_city $ 30 + site_state $ 18 + site_zip $ 8 + owner_type $ 5 + owner_code 8 + adv_notice $ 4 + safety_hlth $ 8 + sic_code $ 8 + naics_code $ 6 + insp_type $ 6 + insp_scope $ 6 + why_no_insp $ 1 + union_status $ 1 + safety_manuf $ 1 + safety_const $ 1 + safety_marit $ 1 + health_manuf $ 1 + health_const $ 1 + health_marit $ 1 + migrant $ 1 + mail_street $ 110 + mail_city $ 30 + mail_state $ 2 + mail_zip 8 + host_est_key $ 18 + nr_in_estab 8 + open_date 8 + case_mod_date 8 + close_conf_date 8 + close_case_date 8 + open_year 8 + case_mod_year 8 + close_conf_year 8 + close_case_year 8 + osha_accident_indicator $ 1 + violation_type_s $ 1 + violation_type_o $ 1 + violation_type_r $ 1 + violation_type_u $ 1 + violation_type_w $ 1 + inspection_to_filter $ 1 ; + FORMAT + activity_nr BEST9. + reporting_id BEST7. + state_flag $CHAR1. + estab_name $CHAR100. + site_address $CHAR142. + site_city $CHAR30. + site_state $CHAR18. + site_zip $CHAR8. + owner_type $CHAR5. + owner_code BEST5. + adv_notice $CHAR4. + safety_hlth $CHAR8. + sic_code $CHAR8. + naics_code $CHAR6. + insp_type $CHAR6. + insp_scope $CHAR6. + why_no_insp $CHAR1. + union_status $CHAR1. + safety_manuf $CHAR1. + safety_const $CHAR1. + safety_marit $CHAR1. + health_manuf $CHAR1. + health_const $CHAR1. + health_marit $CHAR1. + migrant $CHAR1. + mail_street $CHAR110. + mail_city $CHAR30. + mail_state $CHAR2. + mail_zip BEST5. + host_est_key $CHAR18. + nr_in_estab BEST5. + open_date YYMMDD10. + case_mod_date YYMMDD10. + close_conf_date YYMMDD10. + close_case_date YYMMDD10. + open_year BEST4. + case_mod_year BEST4. + close_conf_year BEST4. + close_case_year BEST4. + osha_accident_indicator $CHAR1. + violation_type_s $CHAR1. + violation_type_o $CHAR1. + violation_type_r $CHAR1. + violation_type_u $CHAR1. + violation_type_w $CHAR1. + inspection_to_filter $CHAR1. ; + INFORMAT + activity_nr BEST9. + reporting_id BEST7. + state_flag $CHAR1. + estab_name $CHAR100. + site_address $CHAR142. + site_city $CHAR30. + site_state $CHAR18. + site_zip $CHAR8. + owner_type $CHAR5. + owner_code BEST5. + adv_notice $CHAR4. + safety_hlth $CHAR8. + sic_code $CHAR8. + naics_code $CHAR6. + insp_type $CHAR6. + insp_scope $CHAR6. + why_no_insp $CHAR1. + union_status $CHAR1. + safety_manuf $CHAR1. + safety_const $CHAR1. + safety_marit $CHAR1. + health_manuf $CHAR1. + health_const $CHAR1. + health_marit $CHAR1. + migrant $CHAR1. + mail_street $CHAR110. + mail_city $CHAR30. + mail_state $CHAR2. + mail_zip BEST5. + host_est_key $CHAR18. + nr_in_estab BEST5. + open_date YYMMDD10. + case_mod_date YYMMDD10. + close_conf_date YYMMDD10. + close_case_date YYMMDD10. + open_year BEST4. + case_mod_year BEST4. + close_conf_year BEST4. + close_case_year BEST4. + osha_accident_indicator $CHAR1. + violation_type_s $CHAR1. + violation_type_o $CHAR1. + violation_type_r $CHAR1. + violation_type_u $CHAR1. + violation_type_w $CHAR1. + inspection_to_filter $CHAR1. ; + LABEL + activity_nr='Activity NR' + reporting_id='Reporting ID' + state_flag='State Flag' + estab_name='Establishment Name' + site_address='Site Street Address' + site_city='Site City' + site_state='Site State' + site_zip='Site Zip Code' + owner_type='Owner Type' + owner_code='Owner Code' + adv_notice='Advance Notice' + safety_hlth='Safety/Health' + sic_code='SIC' + naics_code='NAICS Code' + insp_type='Inspection Type' + insp_scope='Inspection Scope' + why_no_insp='Why No Inspection' + union_status='Union Status' + safety_manuf='Safety Manufacturing' + safety_const='Safety Construction' + safety_marit='Safety Maritime' + health_manuf='Health Manufacturing' + health_const='Health Construction' + health_marit='Health Maritime' + migrant='Migrant' + mail_street='Mailing Street Address' + mail_city='Mailing Address City' + mail_state='Mailing Address State' + mail_zip='Mailing Address Zip Code' + host_est_key='Host Establishment Key' + nr_in_estab='Number of Employees' + open_date='Open Date' + case_mod_date='Case Modification Date' + close_conf_date='Closing Conference Date' + close_case_date='Case Closed Date'; + INFILE DATALINES + DLM=',' + MISSOVER + DSD ; + INPUT + activity_nr : ?? BEST9. + reporting_id : ?? BEST7. + state_flag : $CHAR1. + estab_name : $CHAR100. + site_address : $CHAR142. + site_city : $CHAR30. + site_state : $CHAR18. + site_zip : $CHAR8. + owner_type : $CHAR5. + owner_code : ?? BEST5. + adv_notice : $CHAR4. + safety_hlth : $CHAR8. + sic_code : $CHAR8. + naics_code : $CHAR6. + insp_type : $CHAR6. + insp_scope : $CHAR6. + why_no_insp : $CHAR1. + union_status : $CHAR1. + safety_manuf : $CHAR1. + safety_const : $CHAR1. + safety_marit : $CHAR1. + health_manuf : $CHAR1. + health_const : $CHAR1. + health_marit : $CHAR1. + migrant : $CHAR1. + mail_street : $CHAR110. + mail_city : $CHAR30. + mail_state : $CHAR2. + mail_zip : ?? BEST5. + host_est_key : $CHAR18. + nr_in_estab : ?? BEST5. + open_date : ?? YYMMDD10. + case_mod_date : ?? YYMMDD10. + close_conf_date : ?? YYMMDD10. + close_case_date : ?? YYMMDD10. + open_year : ?? BEST4. + case_mod_year : ?? BEST4. + close_conf_year : ?? BEST4. + close_case_year : ?? BEST4. + osha_accident_indicator : $CHAR1. + violation_type_s : $CHAR1. + violation_type_o : $CHAR1. + violation_type_r : $CHAR1. + violation_type_u : $CHAR1. + violation_type_w : $CHAR1. + inspection_to_filter : $CHAR1. ; +DATALINES; +900001,4001,C,Acme Steel Fabrication,100 Industrial Pkwy,Springfield,IL,62701,B,3001,N,S,3441,331222,A,U,N,N,Y,N,N,N,N,N,N,100 Industrial Pkwy,Springfield,IL,62701,HK0001,85,2015-03-02,,2015-03-20,2015-03-25,2015,,2015,2015,N,Y,N,N,N,N,N +900002,4002,T,Lonestar Roofing Co,200 Main St,Austin,TX,73301,B,3002,Y,S,1761,238160,B,C,N,N,Y,N,N,N,N,N,N,200 Main St,Austin,TX,73301,HK0002,22,2015-04-11,,2015-04-30,2015-05-02,2015,,2015,2015,Y,N,Y,N,N,N,N +900003,4003,O,Buckeye Chemical Plant,300 River Rd,Columbus,OH,43201,A,3003,N,H,2911,325110,A,U,Y,Y,N,Y,Y,N,N,N,N,300 River Rd,Columbus,OH,43201,HK0003,410,2015-05-19,2015-06-01,2015-06-10,2015-06-15,2015,2015,2015,2015,N,N,N,Y,N,N,N +900004,4004,N,Empire Electrical Contractors,400 Broadway,Albany,NY,12201,B,3004,N,S,1731,238210,A,C,N,N,Y,N,N,N,N,N,N,400 Broadway,Albany,NY,12201,HK0004,18,2015-06-08,,2015-06-25,2015-06-28,2015,,2015,2015,N,Y,N,N,N,N,N +900005,4005,P,Keystone Trenching LLC,500 Canal St,Pittsburgh,PA,15201,B,3005,N,S,1611,238910,A,U,Y,N,Y,N,N,N,N,N,N,500 Canal St,Pittsburgh,PA,15201,HK0005,35,2015-07-14,2015-07-20,2015-08-01,2015-08-05,2015,2015,2015,2015,Y,Y,N,N,N,N,N +900006,4006,I,Prairie Forklift Services,600 Grain Ave,Peoria,IL,61601,B,3006,N,S,4213,484220,B,C,N,N,Y,N,N,N,N,N,N,600 Grain Ave,Peoria,IL,61601,HK0006,12,2015-08-22,,2015-09-05,2015-09-08,2015,,2015,2015,N,N,N,N,Y,N,N +900007,4007,L,Bayou Petrochemical Works,700 Refinery Rd,Baton Rouge,LA,70801,A,3007,N,H,2911,324110,A,U,Y,Y,N,Y,Y,N,N,N,N,700 Refinery Rd,Baton Rouge,LA,70801,HK0007,620,2015-09-30,2015-10-10,2015-10-20,2015-10-25,2015,2015,2015,2015,N,N,N,Y,N,N,N +900008,4008,F,Sunshine Roof & Deck,800 Palm Ave,Orlando,FL,32801,B,3008,Y,S,1761,238160,B,C,N,N,Y,N,N,N,N,N,N,800 Palm Ave,Orlando,FL,32801,HK0008,27,2015-10-12,,2015-10-28,2015-11-02,2015,,2015,2015,Y,N,Y,N,N,N,N +900009,4009,W,Cascade Crane & Rigging,900 Harbor Blvd,Seattle,WA,98101,B,3009,N,S,1794,238190,A,C,N,N,Y,N,N,N,N,N,N,900 Harbor Blvd,Seattle,WA,98101,HK0009,44,2015-11-05,,2015-11-20,2015-11-24,2015,,2015,2015,N,Y,N,N,N,N,N +900010,4010,M,Great Lakes Welding Inc,1000 Dock St,Detroit,MI,48201,B,3010,N,S,3441,332313,B,U,N,N,Y,N,N,N,N,N,N,1000 Dock St,Detroit,MI,48201,HK0010,31,2015-12-01,,2015-12-15,2015-12-19,2015,,2015,2015,N,N,N,N,N,Y,N +; +RUN; + +PROC PRINT DATA=WORK.osha_inspection LABEL; + VAR activity_nr estab_name site_city site_state insp_type open_date close_case_date; + TITLE "OSHA Inspection import (Jenner compatibility bundle)"; +RUN; + +PROC FREQ DATA=WORK.osha_inspection; + TABLES insp_type owner_type; + TITLE "OSHA Inspection: insp_type and owner_type distribution"; +RUN; diff --git a/jenner-check/t003_osha_violation/autoexec.sas b/jenner-check/t003_osha_violation/autoexec.sas new file mode 100644 index 0000000..719f93f --- /dev/null +++ b/jenner-check/t003_osha_violation/autoexec.sas @@ -0,0 +1,2 @@ +/* cap input rows for the captured run */ +options obs=100; diff --git a/jenner-check/t003_osha_violation/expected.json b/jenner-check/t003_osha_violation/expected.json new file mode 100644 index 0000000..59ffbd4 --- /dev/null +++ b/jenner-check/t003_osha_violation/expected.json @@ -0,0 +1,18 @@ +{ + "_captured_at": "2026-08-19T05:46:41Z", + "_captured_run_id": "r_57ddb1ff69194f8fb1fd94c6621f06b4", + "status": "ok", + "exit_code": 0, + "log_contains": [ + "NOTE: Wrote WORK.osha_violation (10 rows, 28 columns).", + "NOTE: PROC PRINT completed: 10 observations printed, 6 variables" + ], + "log_does_not_contain": [ + "ERROR:", + "[JENNER-ERROR" + ], + "diagnostics": { + "parse_warnings": [], + "runtime_warnings": [] + } +} diff --git a/jenner-check/t003_osha_violation/expected/log.txt b/jenner-check/t003_osha_violation/expected/log.txt new file mode 100644 index 0000000..8452ae7 --- /dev/null +++ b/jenner-check/t003_osha_violation/expected/log.txt @@ -0,0 +1,15 @@ +NOTE: Option OBS changed to 100. +NOTE: DATA WORK.osha_violation + +NOTE: Processing inline DATALINES (10 lines) + +NOTE: Read 10 rows from DATALINES. +NOTE: Wrote WORK.osha_violation (10 rows, 28 columns). +NOTE: DATA elapsed: + wall 0.00 seconds + cpu 0.00 seconds +NOTE: PROC PRINT data=WORK.osha_violation + +NOTE: PROC PRINT completed: 10 observations printed, 6 variables +NOTE: PROC MEANS +NOTE: PROC MEANS statement used. diff --git a/jenner-check/t003_osha_violation/expected/output.txt b/jenner-check/t003_osha_violation/expected/output.txt new file mode 100644 index 0000000..e574d34 --- /dev/null +++ b/jenner-check/t003_osha_violation/expected/output.txt @@ -0,0 +1,24 @@ + OSHA Violation import (Jenner compatibility bundle) + + Obs Activity NR Citation ID Standard Violation Type Current Penalty Gravity + 1 900001 C001001 1926.451(g)(1) S 5000 4 + 2 900001 C001002 1910.132(d)(1) S 1200 3 + 3 900002 C002001 1926.501(b)(13) S 3500 3 + 4 900003 C003001 1910.1200(h)(1) H 2800 2 + 5 900003 C003002 1910.134(e)(1) H 1000 2 + 6 900004 C004001 1926.416(a)(1) S 6300 4 + 7 900005 C005001 1926.652(a)(1) S 9400 4 + 8 900006 C006001 1910.178(l)(1) S 2100 2 + 9 900007 C007001 1910.119(d)(1) H 11700 5 + 10 900008 C008001 1926.451(g)(1) S 4200 3 + + OSHA Violation: penalty summary statistics + + The MEANS Procedure + + Variable Label Sum Mean N + ---------------------------------------------------------------------------- + current_penalty Current Penalty 47200.0000000 4720.0000000 10 + initial_penalty Initial Penalty 56600.0000000 5660.0000000 10 + ---------------------------------------------------------------------------- + diff --git a/jenner-check/t003_osha_violation/meta.json b/jenner-check/t003_osha_violation/meta.json new file mode 100644 index 0000000..e9e91f8 --- /dev/null +++ b/jenner-check/t003_osha_violation/meta.json @@ -0,0 +1,7 @@ +{ + "bundle": "t003_osha_violation", + "source_file": "SAS/osha_violation_sas.sas", + "source_blob_sha": "0214567c49ae7c0ca5384704b123763b5bcb358d", + "source_commit": "ddcc1c32fab05c755c42622b4c42dbe857a665e8", + "notes": "Imports the OSHA citation/violation layout, including COMMA10. currency informats for current_penalty/initial_penalty/fta_penalty and YYMMDD10. dates. Original reads osha_violation.csv via &location/INFILE; this bundle replaces that with 10 inline DATALINES rows of synthetic citations (comma-delimited variant the script documents) and adds a PROC PRINT + PROC MEANS to sum/average the penalty fields. All LENGTH/FORMAT/INFORMAT/LABEL/INPUT statements are unmodified from the source." +} diff --git a/jenner-check/t003_osha_violation/script.sas b/jenner-check/t003_osha_violation/script.sas new file mode 100644 index 0000000..685b83a --- /dev/null +++ b/jenner-check/t003_osha_violation/script.sas @@ -0,0 +1,190 @@ +/* -------------------------------------------------------------------- + IMPORT OSHA VIOLATION FILE INTO SAS DATASET + (Jenner compatibility bundle — adapted from SAS/osha_violation_sas.sas) + + Original input: osha_violation.csv, downloaded from + http://ogesdw.dol.gov/views/data_summary.php via the &location macro + variable and an INFILE statement. + + Adaptation for this bundle only: the INFILE/&location mechanism is + replaced with an inline DATALINES block of 10 synthetic citation rows + shaped to the same LENGTH/FORMAT/INFORMAT/INPUT layout as the original + script, using the comma-delimited variant the original script + documents as its non-Unix alternative (DLM=',' MISSOVER DSD). Every + LENGTH/FORMAT/INFORMAT/LABEL/INPUT statement below is copied + unmodified from the original file. + -------------------------------------------------------------------- */ + +DATA WORK.osha_violation; + LENGTH + activity_nr 8 + citation_id $ 7 + delete_flag $ 1 + standard $ 22 + viol_type $ 1 + issuance_date 8 + abate_date 8 + abate_complete $ 1 + current_penalty 8 + initial_penalty 8 + contest_date 8 + final_order_date 8 + nr_instances 8 + nr_exposed 8 + rec $ 1 + gravity 8 + emphasis $ 1 + hazcat $ 10 + fta_insp_nr 8 + fta_issuance_date 8 + fta_penalty 8 + fta_contest_date 8 + fta_final_order_date 8 + hazsub1 $ 4 + hazsub2 $ 4 + hazsub3 $ 4 + hazsub4 $ 4 + hazsub5 $ 4 ; + FORMAT + activity_nr BEST9. + citation_id $CHAR7. + delete_flag $CHAR1. + standard $CHAR22. + viol_type $CHAR1. + issuance_date YYMMDD10. + abate_date YYMMDD10. + abate_complete $CHAR1. + current_penalty BEST10. + initial_penalty BEST10. + contest_date YYMMDD10. + final_order_date YYMMDD10. + nr_instances BEST5. + nr_exposed BEST5. + rec $CHAR1. + gravity BEST2. + emphasis $CHAR1. + hazcat $CHAR10. + fta_insp_nr BEST9. + fta_issuance_date YYMMDD10. + fta_penalty BEST10. + fta_contest_date YYMMDD10. + fta_final_order_date YYMMDD10. + hazsub1 $CHAR4. + hazsub2 $CHAR4. + hazsub3 $CHAR4. + hazsub4 $CHAR4. + hazsub5 $CHAR4. ; + INFORMAT + activity_nr BEST9. + citation_id $CHAR7. + delete_flag $CHAR1. + standard $CHAR22. + viol_type $CHAR1. + issuance_date YYMMDD10. + abate_date YYMMDD10. + abate_complete $CHAR1. + current_penalty BEST10. + initial_penalty BEST10. + contest_date YYMMDD10. + final_order_date YYMMDD10. + nr_instances BEST5. + nr_exposed BEST5. + rec $CHAR1. + gravity BEST2. + emphasis $CHAR1. + hazcat $CHAR10. + fta_insp_nr BEST9. + fta_issuance_date YYMMDD10. + fta_penalty BEST10. + fta_contest_date YYMMDD10. + fta_final_order_date YYMMDD10. + hazsub1 $CHAR4. + hazsub2 $CHAR4. + hazsub3 $CHAR4. + hazsub4 $CHAR4. + hazsub5 $CHAR4. ; + LABEL + activity_nr='Activity NR' + citation_id='Citation ID' + delete_flag='Delete Flag' + standard='Standard' + viol_type='Violation Type' + issuance_date='Issuance Date' + abate_date='Abate Date' + abate_complete='Abate Complete' + current_penalty='Current Penalty' + initial_penalty='Initial Penalty' + contest_date='Contested Date' + final_order_date='Final Order Date' + nr_instances='Number of Instances' + nr_exposed='Number Exposed' + rec='Rec' + gravity='Gravity' + emphasis='Emphasis' + hazcat='Hazard Category' + fta_insp_nr='FTA Inspection NR' + fta_issuance_date='FTA Issuance Date' + fta_penalty='FTA Current Penalty' + fta_contest_date='FTA Contested Date' + fta_final_order_date='FTA Final Order Date' + hazsub1='Hazardous Substance 1' + hazsub2='Hazardous Substance 2' + hazsub3='Hazardous Substance 3' + hazsub4='Hazardous Substance 4' + hazsub5='Hazardous Substance 5'; + INFILE DATALINES + DLM=',' + MISSOVER + DSD ; + INPUT + activity_nr : ?? BEST9. + citation_id : $CHAR7. + delete_flag : $CHAR1. + standard : $CHAR22. + viol_type : $CHAR1. + issuance_date : ?? YYMMDD10. + abate_date : ?? YYMMDD10. + abate_complete : $CHAR1. + current_penalty : ?? COMMA10. + initial_penalty : ?? COMMA10. + contest_date : ?? YYMMDD10. + final_order_date : ?? YYMMDD10. + nr_instances : ?? BEST5. + nr_exposed : ?? BEST5. + rec : $CHAR1. + gravity : ?? BEST2. + emphasis : $CHAR1. + hazcat : $CHAR10. + fta_insp_nr : ?? BEST9. + fta_issuance_date : ?? YYMMDD10. + fta_penalty : ?? COMMA10. + fta_contest_date : ?? YYMMDD10. + fta_final_order_date : ?? YYMMDD10. + hazsub1 : $CHAR4. + hazsub2 : $CHAR4. + hazsub3 : $CHAR4. + hazsub4 : $CHAR4. + hazsub5 : $CHAR4. ; +DATALINES; +900001,C001001,N,1926.451(g)(1),S,2015-03-20,2015-04-20,Y,"5,000","7,000",,,1,3,,4,,FALLS,,,,,,,,,, +900001,C001002,N,1910.132(d)(1),S,2015-03-20,2015-04-15,Y,"1,200","1,200",,,1,1,,3,,PPE,,,,,,,,,, +900002,C002001,N,1926.501(b)(13),S,2015-04-30,2015-05-30,N,"3,500","5,000",2015-05-15,2015-06-01,1,1,,3,,FALLS,,,,,,,,,, +900003,C003001,N,1910.1200(h)(1),H,2015-06-10,2015-07-10,Y,"2,800","2,800",,,1,5,,2,X,CHEM,900010,2015-05-19,"1,500",,,BENZ,,,, +900003,C003002,N,1910.134(e)(1),H,2015-06-10,2015-07-10,Y,"1,000","1,000",,,1,5,,2,,RESP,,,,,,,,,, +900004,C004001,N,1926.416(a)(1),S,2015-06-25,2015-07-25,Y,"6,300","6,300",,,1,1,,4,,ELEC,,,,,,,,,, +900005,C005001,N,1926.652(a)(1),S,2015-08-01,2015-08-15,N,"9,400","12,000",2015-08-10,2015-09-01,1,1,,4,,TRENCH,900011,2015-07-14,"2,200",,,,,,, +900006,C006001,N,1910.178(l)(1),S,2015-09-05,2015-09-25,Y,"2,100","2,100",,,1,1,,2,,FORKLIFT,,,,,,,,,, +900007,C007001,N,1910.119(d)(1),H,2015-10-20,2015-11-20,N,"11,700","15,000",2015-11-05,2015-12-01,2,3,,5,X,PSM,900013,2015-09-30,"3,900",,,H2S,VOC,,, +900008,C008001,N,1926.451(g)(1),S,2015-10-28,2015-11-28,Y,"4,200","4,200",,,1,2,,3,,FALLS,,,,,,,,,, +; +RUN; + +PROC PRINT DATA=WORK.osha_violation LABEL; + VAR activity_nr citation_id standard viol_type current_penalty gravity; + TITLE "OSHA Violation import (Jenner compatibility bundle)"; +RUN; + +PROC MEANS DATA=WORK.osha_violation SUM MEAN N; + VAR current_penalty initial_penalty; + TITLE "OSHA Violation: penalty summary statistics"; +RUN; diff --git a/jenner-check/t004_osha_accident_abstract/autoexec.sas b/jenner-check/t004_osha_accident_abstract/autoexec.sas new file mode 100644 index 0000000..719f93f --- /dev/null +++ b/jenner-check/t004_osha_accident_abstract/autoexec.sas @@ -0,0 +1,2 @@ +/* cap input rows for the captured run */ +options obs=100; diff --git a/jenner-check/t004_osha_accident_abstract/expected.json b/jenner-check/t004_osha_accident_abstract/expected.json new file mode 100644 index 0000000..10f2665 --- /dev/null +++ b/jenner-check/t004_osha_accident_abstract/expected.json @@ -0,0 +1,18 @@ +{ + "_captured_at": "2026-08-19T05:46:41Z", + "_captured_run_id": "r_b65c288341f34c2e80de6da33b8b02c1", + "status": "ok", + "exit_code": 0, + "log_contains": [ + "NOTE: Wrote WORK.osha_accident_abstract (10 rows, 3 columns).", + "NOTE: PROC PRINT completed: 10 observations printed, 3 variables" + ], + "log_does_not_contain": [ + "ERROR:", + "[JENNER-ERROR" + ], + "diagnostics": { + "parse_warnings": [], + "runtime_warnings": [] + } +} diff --git a/jenner-check/t004_osha_accident_abstract/expected/log.txt b/jenner-check/t004_osha_accident_abstract/expected/log.txt new file mode 100644 index 0000000..fdc1fed --- /dev/null +++ b/jenner-check/t004_osha_accident_abstract/expected/log.txt @@ -0,0 +1,21 @@ +NOTE: Option OBS changed to 100. +NOTE: DATA WORK.osha_accident_abstract + +NOTE: Processing inline DATALINES (10 lines) + +NOTE: Read 10 rows from DATALINES. +NOTE: Wrote WORK.osha_accident_abstract (10 rows, 3 columns). +NOTE: DATA elapsed: + wall 0.00 seconds + cpu 0.00 seconds +NOTE: PROC PRINT data=WORK.osha_accident_abstract + +NOTE: PROC PRINT completed: 10 observations printed, 3 variables +NOTE: PROC SORT data=WORK.osha_accident_abstract + +NOTE: Read 10 rows from WORK.osha_accident_abstract. +NOTE: Wrote WORK.abstract_sorted (10 rows, 3 columns). +NOTE: PROC SORT statement used. +NOTE: PROC PRINT data=WORK.abstract_sorted + +NOTE: PROC PRINT completed: 10 observations printed, 3 variables diff --git a/jenner-check/t004_osha_accident_abstract/expected/output.txt b/jenner-check/t004_osha_accident_abstract/expected/output.txt new file mode 100644 index 0000000..1a73849 --- /dev/null +++ b/jenner-check/t004_osha_accident_abstract/expected/output.txt @@ -0,0 +1,28 @@ + OSHA Accident Abstract import (Jenner compatibility bundle) + + Obs Summary NR Line NR Abstract text + 1 100001 1 Employee #1 was installing roof sheathing on new construction when + 2 100001 2 he lost his balance and fell approximately 14 feet to the ground. + 3 100002 1 Employee #1 was struck by a section of steel pipe that fell from a + 4 100002 2 forklift being operated nearby. Employee sustained head injury. + 5 100003 1 Employee #1 reached into an unguarded conveyor to clear a jam and + 6 100003 2 his glove was caught, pulling his hand into the machine. + 7 100004 1 Employee #1 was performing electrical repairs on an energized panel + 8 100004 2 without lockout/tagout and received an electrical shock. + 9 100005 1 Employee #1 was working in a 12-foot trench that had not been + 10 100005 2 shored when the trench wall collapsed, burying him to the chest. + + OSHA Accident Abstract: sorted by summary_nr/line_nr + + Obs Summary NR Line NR Abstract text + 1 100001 1 Employee #1 was installing roof sheathing on new construction when + 2 100001 2 he lost his balance and fell approximately 14 feet to the ground. + 3 100002 1 Employee #1 was struck by a section of steel pipe that fell from a + 4 100002 2 forklift being operated nearby. Employee sustained head injury. + 5 100003 1 Employee #1 reached into an unguarded conveyor to clear a jam and + 6 100003 2 his glove was caught, pulling his hand into the machine. + 7 100004 1 Employee #1 was performing electrical repairs on an energized panel + 8 100004 2 without lockout/tagout and received an electrical shock. + 9 100005 1 Employee #1 was working in a 12-foot trench that had not been + 10 100005 2 shored when the trench wall collapsed, burying him to the chest. + diff --git a/jenner-check/t004_osha_accident_abstract/meta.json b/jenner-check/t004_osha_accident_abstract/meta.json new file mode 100644 index 0000000..9786ecc --- /dev/null +++ b/jenner-check/t004_osha_accident_abstract/meta.json @@ -0,0 +1,7 @@ +{ + "bundle": "t004_osha_accident_abstract", + "source_file": "SAS/osha_accident_abstract_sas.sas", + "source_blob_sha": "7cb66bde30fea3a810dc6c5d8e3a70f569d4779e", + "source_commit": "ddcc1c32fab05c755c42622b4c42dbe857a665e8", + "notes": "Imports the OSHA accident-narrative layout, the smallest of the ten templates (3 columns: summary_nr, line_nr, free-text abstract_text). Original reads osha_accident_abstract.csv via &location/INFILE; this bundle replaces that with 10 inline DATALINES rows of synthetic multi-line accident narratives (comma-delimited variant the script documents) and adds a PROC PRINT + PROC SORT to exercise ordering by summary_nr/line_nr. All LENGTH/FORMAT/INFORMAT/LABEL/INPUT statements are unmodified from the source." +} diff --git a/jenner-check/t004_osha_accident_abstract/script.sas b/jenner-check/t004_osha_accident_abstract/script.sas new file mode 100644 index 0000000..552d419 --- /dev/null +++ b/jenner-check/t004_osha_accident_abstract/script.sas @@ -0,0 +1,68 @@ +/* -------------------------------------------------------------------- + IMPORT OSHA ACCIDENT ABSTRACT FILE INTO SAS DATASET + (Jenner compatibility bundle — adapted from + SAS/osha_accident_abstract_sas.sas) + + Original input: osha_accident_abstract.csv, downloaded from + http://ogesdw.dol.gov/views/data_summary.php via the &location macro + variable and an INFILE statement. + + Adaptation for this bundle only: the INFILE/&location mechanism is + replaced with an inline DATALINES block of 10 synthetic narrative + rows shaped to the same LENGTH/FORMAT/INFORMAT/INPUT layout as the + original script, using the comma-delimited variant the original + script documents as its non-Unix alternative (DLM=',' MISSOVER DSD). + Every LENGTH/FORMAT/INFORMAT/LABEL/INPUT statement below is copied + unmodified from the original file. + -------------------------------------------------------------------- */ + +DATA WORK.osha_accident_abstract; + LENGTH + summary_nr 8 + line_nr 4 + abstract_text $ 80 ; + FORMAT + summary_nr BEST9. + line_nr BEST4. + abstract_text $CHAR80. ; + INFORMAT + summary_nr BEST9. + line_nr BEST4. + abstract_text $CHAR80. ; + LABEL + summary_nr='Summary NR' + line_nr='Line NR' + abstract_text='Abstract text' ; + INFILE DATALINES + DLM=',' + MISSOVER + DSD ; + INPUT + summary_nr : ?? BEST9. + line_nr : ?? BEST4. + abstract_text : $CHAR80. ; +DATALINES; +100001,1,"Employee #1 was installing roof sheathing on new construction when" +100001,2,"he lost his balance and fell approximately 14 feet to the ground." +100002,1,"Employee #1 was struck by a section of steel pipe that fell from a" +100002,2,"forklift being operated nearby. Employee sustained head injury." +100003,1,"Employee #1 reached into an unguarded conveyor to clear a jam and" +100003,2,"his glove was caught, pulling his hand into the machine." +100004,1,"Employee #1 was performing electrical repairs on an energized panel" +100004,2,"without lockout/tagout and received an electrical shock." +100005,1,"Employee #1 was working in a 12-foot trench that had not been" +100005,2,"shored when the trench wall collapsed, burying him to the chest." +; +RUN; + +PROC PRINT DATA=WORK.osha_accident_abstract LABEL; + TITLE "OSHA Accident Abstract import (Jenner compatibility bundle)"; +RUN; + +PROC SORT DATA=WORK.osha_accident_abstract OUT=WORK.abstract_sorted; + BY summary_nr line_nr; +RUN; + +PROC PRINT DATA=WORK.abstract_sorted LABEL; + TITLE "OSHA Accident Abstract: sorted by summary_nr/line_nr"; +RUN; diff --git a/jenner-check/t005_osha_related_activity/autoexec.sas b/jenner-check/t005_osha_related_activity/autoexec.sas new file mode 100644 index 0000000..719f93f --- /dev/null +++ b/jenner-check/t005_osha_related_activity/autoexec.sas @@ -0,0 +1,2 @@ +/* cap input rows for the captured run */ +options obs=100; diff --git a/jenner-check/t005_osha_related_activity/expected.json b/jenner-check/t005_osha_related_activity/expected.json new file mode 100644 index 0000000..d0b4696 --- /dev/null +++ b/jenner-check/t005_osha_related_activity/expected.json @@ -0,0 +1,18 @@ +{ + "_captured_at": "2026-08-19T05:46:41Z", + "_captured_run_id": "r_cb8e4ac5709246308b5d7267dc4d1c8b", + "status": "ok", + "exit_code": 0, + "log_contains": [ + "NOTE: Wrote WORK.osha_related_activity (10 rows, 5 columns).", + "NOTE: PROC PRINT completed: 10 observations printed, 5 variables" + ], + "log_does_not_contain": [ + "ERROR:", + "[JENNER-ERROR" + ], + "diagnostics": { + "parse_warnings": [], + "runtime_warnings": [] + } +} diff --git a/jenner-check/t005_osha_related_activity/expected/log.txt b/jenner-check/t005_osha_related_activity/expected/log.txt new file mode 100644 index 0000000..49f6646 --- /dev/null +++ b/jenner-check/t005_osha_related_activity/expected/log.txt @@ -0,0 +1,15 @@ +NOTE: Option OBS changed to 100. +NOTE: DATA WORK.osha_related_activity + +NOTE: Processing inline DATALINES (10 lines) + +NOTE: Read 10 rows from DATALINES. +NOTE: Wrote WORK.osha_related_activity (10 rows, 5 columns). +NOTE: DATA elapsed: + wall 0.00 seconds + cpu 0.00 seconds +NOTE: PROC PRINT data=WORK.osha_related_activity + +NOTE: PROC PRINT completed: 10 observations printed, 5 variables +NOTE: PROC FREQ +NOTE: PROC FREQ statement used. diff --git a/jenner-check/t005_osha_related_activity/expected/output.txt b/jenner-check/t005_osha_related_activity/expected/output.txt new file mode 100644 index 0000000..7bb1bbb --- /dev/null +++ b/jenner-check/t005_osha_related_activity/expected/output.txt @@ -0,0 +1,35 @@ + OSHA Related Activity import (Jenner compatibility bundle) + + Obs Activity NR Related Activity Type Related Activity NR Related Activity Safety Related Activitiy Health + 1 900001 R 900002 Y N + 2 900002 R 900001 Y N + 3 900003 C 900004 N Y + 4 900004 C 900003 N Y + 5 900005 R 900006 Y N + 6 900006 R 900005 Y N + 7 900007 C . N Y + 8 900008 R 900009 Y N + 9 900009 R 900008 Y N + 10 900010 C . N Y + + OSHA Related Activity: type/safety/health distribution + + The FREQ Procedure + + Cumulative Cumulative +Related Activity Type Frequency Percent Frequency Percent +------------------------------------------------------------------------------ +C 4 40.00 4 40.00 +R 6 60.00 10 100.00 + + Cumulative Cumulative +Related Activity Safety Frequency Percent Frequency Percent +-------------------------------------------------------------------------------- +N 4 40.00 4 40.00 +Y 6 60.00 10 100.00 + + Cumulative Cumulative +Related Activitiy Health Frequency Percent Frequency Percent +--------------------------------------------------------------------------------- +N 6 60.00 6 60.00 +Y 4 40.00 10 100.00 diff --git a/jenner-check/t005_osha_related_activity/meta.json b/jenner-check/t005_osha_related_activity/meta.json new file mode 100644 index 0000000..b35f374 --- /dev/null +++ b/jenner-check/t005_osha_related_activity/meta.json @@ -0,0 +1,7 @@ +{ + "bundle": "t005_osha_related_activity", + "source_file": "SAS/osha_related_activity_sas.sas", + "source_blob_sha": "be8fd3fd12d0651b04b85e506e83f9d02a7cc2b9", + "source_commit": "ddcc1c32fab05c755c42622b4c42dbe857a665e8", + "notes": "Imports the OSHA related-activity cross-reference layout (5 columns, all short char/numeric fields, no dates). Original reads osha_related_activity.csv via &location/INFILE; this bundle replaces that with 10 inline DATALINES rows of synthetic related/child-activity links (comma-delimited variant the script documents) and adds a PROC PRINT + PROC FREQ. All LENGTH/FORMAT/INFORMAT/LABEL/INPUT statements are unmodified from the source." +} diff --git a/jenner-check/t005_osha_related_activity/script.sas b/jenner-check/t005_osha_related_activity/script.sas new file mode 100644 index 0000000..02786bc --- /dev/null +++ b/jenner-check/t005_osha_related_activity/script.sas @@ -0,0 +1,75 @@ +/* -------------------------------------------------------------------- + IMPORT OSHA RELATED ACTIVITY FILE INTO SAS DATASET + (Jenner compatibility bundle — adapted from + SAS/osha_related_activity_sas.sas) + + Original input: osha_related_activity.csv, downloaded from + http://ogesdw.dol.gov/views/data_summary.php via the &location macro + variable and an INFILE statement. + + Adaptation for this bundle only: the INFILE/&location mechanism is + replaced with an inline DATALINES block of 10 synthetic rows shaped + to the same LENGTH/FORMAT/INFORMAT/INPUT layout as the original + script, using the comma-delimited variant the original script + documents as its non-Unix alternative (DLM=',' MISSOVER DSD). Every + LENGTH/FORMAT/INFORMAT/LABEL/INPUT statement below is copied + unmodified from the original file. + -------------------------------------------------------------------- */ + +DATA WORK.osha_related_activity; + LENGTH + activity_nr 8 + rel_type $ 1 + rel_act_nr 8 + rel_safety $ 1 + rel_health $ 1 ; + FORMAT + activity_nr BEST9. + rel_type $CHAR1. + rel_act_nr BEST9. + rel_safety $CHAR1. + rel_health $CHAR1. ; + INFORMAT + activity_nr BEST9. + rel_type $CHAR1. + rel_act_nr BEST9. + rel_safety $CHAR1. + rel_health $CHAR1. ; + LABEL + activity_nr='Activity NR' + rel_type='Related Activity Type' + rel_act_nr='Related Activity NR' + rel_safety='Related Activity Safety' + rel_health='Related Activitiy Health'; + INFILE DATALINES + DLM=',' + MISSOVER + DSD ; + INPUT + activity_nr : ?? BEST9. + rel_type : $CHAR1. + rel_act_nr : ?? BEST9. + rel_safety : $CHAR1. + rel_health : $CHAR1. ; +DATALINES; +900001,R,900002,Y,N +900002,R,900001,Y,N +900003,C,900004,N,Y +900004,C,900003,N,Y +900005,R,900006,Y,N +900006,R,900005,Y,N +900007,C,,N,Y +900008,R,900009,Y,N +900009,R,900008,Y,N +900010,C,,N,Y +; +RUN; + +PROC PRINT DATA=WORK.osha_related_activity LABEL; + TITLE "OSHA Related Activity import (Jenner compatibility bundle)"; +RUN; + +PROC FREQ DATA=WORK.osha_related_activity; + TABLES rel_type rel_safety rel_health; + TITLE "OSHA Related Activity: type/safety/health distribution"; +RUN; diff --git a/jenner-check/t006_osha_violation_gen_duty_std/autoexec.sas b/jenner-check/t006_osha_violation_gen_duty_std/autoexec.sas new file mode 100644 index 0000000..719f93f --- /dev/null +++ b/jenner-check/t006_osha_violation_gen_duty_std/autoexec.sas @@ -0,0 +1,2 @@ +/* cap input rows for the captured run */ +options obs=100; diff --git a/jenner-check/t006_osha_violation_gen_duty_std/expected.json b/jenner-check/t006_osha_violation_gen_duty_std/expected.json new file mode 100644 index 0000000..ab82911 --- /dev/null +++ b/jenner-check/t006_osha_violation_gen_duty_std/expected.json @@ -0,0 +1,18 @@ +{ + "_captured_at": "2026-08-19T05:46:41Z", + "_captured_run_id": "r_a2340426b5234888ad745c0dff0dae04", + "status": "ok", + "exit_code": 0, + "log_contains": [ + "NOTE: Wrote WORK.osha_violation_gen_duty_std (8 rows, 4 columns).", + "NOTE: PROC PRINT completed: 8 observations printed, 4 variables" + ], + "log_does_not_contain": [ + "ERROR:", + "[JENNER-ERROR" + ], + "diagnostics": { + "parse_warnings": [], + "runtime_warnings": [] + } +} diff --git a/jenner-check/t006_osha_violation_gen_duty_std/expected/log.txt b/jenner-check/t006_osha_violation_gen_duty_std/expected/log.txt new file mode 100644 index 0000000..c617b6c --- /dev/null +++ b/jenner-check/t006_osha_violation_gen_duty_std/expected/log.txt @@ -0,0 +1,21 @@ +NOTE: Option OBS changed to 100. +NOTE: DATA WORK.osha_violation_gen_duty_std + +NOTE: Processing inline DATALINES (8 lines) + +NOTE: Read 8 rows from DATALINES. +NOTE: Wrote WORK.osha_violation_gen_duty_std (8 rows, 4 columns). +NOTE: DATA elapsed: + wall 0.00 seconds + cpu 0.00 seconds +NOTE: PROC PRINT data=WORK.osha_violation_gen_duty_std + +NOTE: PROC PRINT completed: 8 observations printed, 4 variables +NOTE: PROC SORT data=WORK.osha_violation_gen_duty_std + +NOTE: Read 8 rows from WORK.osha_violation_gen_duty_std. +NOTE: Wrote WORK.gds_sorted (8 rows, 4 columns). +NOTE: PROC SORT statement used. +NOTE: PROC PRINT data=WORK.gds_sorted + +NOTE: PROC PRINT completed: 8 observations printed, 4 variables diff --git a/jenner-check/t006_osha_violation_gen_duty_std/expected/output.txt b/jenner-check/t006_osha_violation_gen_duty_std/expected/output.txt new file mode 100644 index 0000000..0ec664c --- /dev/null +++ b/jenner-check/t006_osha_violation_gen_duty_std/expected/output.txt @@ -0,0 +1,24 @@ + OSHA Violation Gen Duty Std import (Jenner compatibility bundle) + + Obs Activity NR Citation ID Line NR Line Text + 1 900003 C003001 1 The employer did not furnish employment free from recognized hazards + 2 900003 C003001 2 that were causing or likely to cause death or serious physical harm. + 3 900003 C003001 3 Employees handling benzene were not provided adequate ventilation. + 4 900007 C007001 1 The employer failed to develop and implement adequate written + 5 900007 C007001 2 procedures for process safety management of highly hazardous + 6 900007 C007001 3 chemicals as required under the General Duty Clause. + 7 900012 C012001 1 Employees working near unguarded rotating machinery parts were + 8 900012 C012001 2 exposed to a recognized amputation hazard during normal operation. + + OSHA Violation Gen Duty Std: sorted narrative lines + + Obs Activity NR Citation ID Line NR Line Text + 1 900003 C003001 1 The employer did not furnish employment free from recognized hazards + 2 900003 C003001 2 that were causing or likely to cause death or serious physical harm. + 3 900003 C003001 3 Employees handling benzene were not provided adequate ventilation. + 4 900007 C007001 1 The employer failed to develop and implement adequate written + 5 900007 C007001 2 procedures for process safety management of highly hazardous + 6 900007 C007001 3 chemicals as required under the General Duty Clause. + 7 900012 C012001 1 Employees working near unguarded rotating machinery parts were + 8 900012 C012001 2 exposed to a recognized amputation hazard during normal operation. + diff --git a/jenner-check/t006_osha_violation_gen_duty_std/meta.json b/jenner-check/t006_osha_violation_gen_duty_std/meta.json new file mode 100644 index 0000000..a61bd00 --- /dev/null +++ b/jenner-check/t006_osha_violation_gen_duty_std/meta.json @@ -0,0 +1,7 @@ +{ + "bundle": "t006_osha_violation_gen_duty_std", + "source_file": "SAS/osha_violation_gen_duty_std_sas.sas", + "source_blob_sha": "488fc9076cb34b0b20c74e62302472a1adccabd4", + "source_commit": "ddcc1c32fab05c755c42622b4c42dbe857a665e8", + "notes": "Imports the OSHA General Duty Clause citation-text layout, notable for its LRECL=18933 sized to very long free-text line_text narratives. Original reads osha_violation_gen_duty_std.csv via &location/INFILE; this bundle replaces that with 8 inline DATALINES rows of synthetic multi-line citation text (comma-delimited variant the script documents) and adds a PROC PRINT + PROC SORT. All LENGTH/FORMAT/INFORMAT/LABEL/INPUT statements are unmodified from the source." +} diff --git a/jenner-check/t006_osha_violation_gen_duty_std/script.sas b/jenner-check/t006_osha_violation_gen_duty_std/script.sas new file mode 100644 index 0000000..1c6567b --- /dev/null +++ b/jenner-check/t006_osha_violation_gen_duty_std/script.sas @@ -0,0 +1,73 @@ +/* -------------------------------------------------------------------- + IMPORT OSHA VIOLATION GEN DUTY STD FILE INTO SAS DATASET + (Jenner compatibility bundle — adapted from + SAS/osha_violation_gen_duty_std_sas.sas) + + Original input: osha_violation_gen_duty_std.csv, downloaded from + http://ogesdw.dol.gov/views/data_summary.php via the &location macro + variable and an INFILE statement. The original LRECL=18933 exists to + accommodate very long free-text General Duty Clause citation + narratives in line_text. + + Adaptation for this bundle only: the INFILE/&location mechanism is + replaced with an inline DATALINES block of 8 synthetic citation-text + rows shaped to the same LENGTH/FORMAT/INFORMAT/INPUT layout as the + original script, using the comma-delimited variant the original + script documents as its non-Unix alternative (DLM=',' MISSOVER DSD). + Every LENGTH/FORMAT/INFORMAT/LABEL/INPUT statement below is copied + unmodified from the original file. + -------------------------------------------------------------------- */ + +DATA WORK.osha_violation_gen_duty_std; + LENGTH + activity_nr 8 + citation_id $ 7 + line_nr 6 + line_text $ 80 ; + FORMAT + activity_nr BEST9. + citation_id $CHAR7. + line_nr BEST6. + line_text $CHAR80. ; + INFORMAT + activity_nr BEST9. + citation_id $CHAR7. + line_nr BEST6. + line_text $CHAR80. ; + LABEL + activity_nr='Activity NR' + citation_id='Citation ID' + line_nr='Line NR' + line_text='Line Text'; + INFILE DATALINES + DLM=',' + MISSOVER + DSD ; + INPUT + activity_nr : ?? BEST9. + citation_id : $CHAR7. + line_nr : ?? BEST6. + line_text : $CHAR80. ; +DATALINES; +900003,C003001,1,"The employer did not furnish employment free from recognized hazards" +900003,C003001,2,"that were causing or likely to cause death or serious physical harm." +900003,C003001,3,"Employees handling benzene were not provided adequate ventilation." +900007,C007001,1,"The employer failed to develop and implement adequate written" +900007,C007001,2,"procedures for process safety management of highly hazardous" +900007,C007001,3,"chemicals as required under the General Duty Clause." +900012,C012001,1,"Employees working near unguarded rotating machinery parts were" +900012,C012001,2,"exposed to a recognized amputation hazard during normal operation." +; +RUN; + +PROC PRINT DATA=WORK.osha_violation_gen_duty_std LABEL; + TITLE "OSHA Violation Gen Duty Std import (Jenner compatibility bundle)"; +RUN; + +PROC SORT DATA=WORK.osha_violation_gen_duty_std OUT=WORK.gds_sorted; + BY activity_nr citation_id line_nr; +RUN; + +PROC PRINT DATA=WORK.gds_sorted LABEL; + TITLE "OSHA Violation Gen Duty Std: sorted narrative lines"; +RUN;