diff --git a/jenner-check/.gitignore b/jenner-check/.gitignore new file mode 100644 index 0000000..2430ca7 --- /dev/null +++ b/jenner-check/.gitignore @@ -0,0 +1,3 @@ +# runner scratch — never commit +*_response.json +response.json diff --git a/jenner-check/README.md b/jenner-check/README.md new file mode 100644 index 0000000..5c03cd5 --- /dev/null +++ b/jenner-check/README.md @@ -0,0 +1,59 @@ +# Jenner compatibility bundles + +This directory was added by a pull request from +[Jenner](https://jenneranalytics.com). It contains a SAS bundle generated +from code already in this repository, so you can run `test/marks.sas` +against the Jenner SAS engine and see the results for yourself. + +## What's in here + +``` +jenner-check/ +├── README.md # this file +├── run_jenner.sh # runner (curl; Linux/macOS/WSL) +├── run_jenner.bat # runner (Windows) +├── run_jenner.sas # runner you can call from base SAS (PROC HTTP) +└── t001_marks_elementary_tests/ + ├── script.sas # test/marks.sas, self-contained + ├── autoexec.sas # options set before the run + ├── input/report.dat # the repo's own data + ├── expected.json # pinned fields from a passing run + ├── expected/ # human-readable snapshot (log, output, file URLs) + └── meta.json # provenance + notes on what was adapted +``` + +`script.sas` is your `test/marks.sas`. The only change is that the +external `infile 'statclass1.dat'` was replaced by inline `datalines` +carrying this repo's own `test/report.dat`, so the bundle is +self-contained. Every PROC, option, and statement is exactly as written +in the original. + +## How to run it + +From inside `jenner-check/`: + +```bash +./run_jenner.sh --all # run every bundle +./run_jenner.sh t001_marks_elementary_tests # run just this one +``` + +On Windows use `run_jenner.bat`. From base SAS (9.4 M5+): + +```sas +%include 'run_jenner.sas'; +%jenner_check_all(); +``` + +Each run prints `status`, `exit_code`, and the captured log; the full +listing and all generated graphics come back as downloadable URLs. + +## Optional: Jenner Compatible badge + +```markdown +[![Jenner Compatible](https://jenneranalytics.com/badges/jenner-compatible.svg)](https://jenneranalytics.com) +``` + +## Don't want future PRs from us? + +Reply with `no-more-prs` anywhere in a comment, or open an issue titled +`jenner-check: opt out`, and we'll stop automated PRs to this repo. diff --git a/jenner-check/run_jenner.bat b/jenner-check/run_jenner.bat new file mode 100644 index 0000000..1039fdf --- /dev/null +++ b/jenner-check/run_jenner.bat @@ -0,0 +1,43 @@ +@echo off +rem run_jenner.bat - Windows runner for Jenner compatibility checks. +rem +rem Usage: run_jenner.bat [response.json] +rem +rem Submits a single .sas file to api.jenneranalytics.com. For +rem bundle-aware mode (autoexec.sas + script.sas concatenation) on +rem Windows, use WSL and invoke run_jenner.sh instead, or wait for the +rem Windows CI runner that will validate a bundle-aware .bat. +rem +rem Output: response.json contains the API response. Read it back in SAS: +rem filename resp 'response.json'; +rem libname resp JSON fileref=resp; +rem proc print data=resp.root; run; +rem +rem Requires: curl.exe (ships with Windows 10+ at C:\Windows\System32). + +setlocal + +if "%~1"=="" ( + echo Usage: %~nx0 ^ [response.json] + exit /b 2 +) + +set SCRIPT=%~1 +set OUT=%~2 +if "%OUT%"=="" set OUT=response.json + +set HOST=api.jenneranalytics.com + +curl.exe -sS -X POST "https://%HOST%/v1/run" ^ + -F "script=@%SCRIPT%;type=application/x-sas" ^ + -F "deterministic=1" ^ + -F "timeout=60" ^ + -o "%OUT%" + +if errorlevel 1 ( + echo curl failed with errorlevel %errorlevel% + exit /b 1 +) + +echo Response written to %OUT% +exit /b 0 diff --git a/jenner-check/run_jenner.sas b/jenner-check/run_jenner.sas new file mode 100644 index 0000000..550e8f8 --- /dev/null +++ b/jenner-check/run_jenner.sas @@ -0,0 +1,526 @@ +/* run_jenner.sas — invoke api.jenneranalytics.com from base SAS. + * + * Requires SAS 9.4 M5 or later (PROC HTTP + libname JSON engine). + * + * --------------------------------------------------------------------------- + * TL;DR for SAS users: + * + * %include 'run_jenner.sas'; + * %jenner_run(script=my_program.sas); / * one script * / + * %jenner_check_all(); / * whole bundle dir * / + * + * --------------------------------------------------------------------------- + * What this file gives you: + * + * %jenner_run — POST one .sas file to the Jenner API, display the + * log + listing + any generated files. + * %jenner_check_all — walk every jenner-check/tNNN_* bundle, + * invoke the API for each, compare the response to + * the bundle's expected.json, produce a summary + * CSV + SAS dataset the repo owner can attach to the + * jenner-check PR. + * + * --------------------------------------------------------------------------- + * How the API call is built: + * + * POST https://api.jenneranalytics.com/v1/run + * Content-Type: multipart/form-data; boundary=... + * + * fields: + * script the .sas source text + * input (repeat) any data files the script reads + * timeout wall-clock seconds, clamped by tier (default 60) + * deterministic "1" to seed RNG and freeze today() + * + * returns JSON: + * run_id, status, exit_code, duration_ms, jenner_version, + * output, log, files[] (each file has path, size_bytes, content_type, + * sha256, optional dataset{rows,columns}) + * + * --------------------------------------------------------------------------- + * If your site has disabled PROC HTTP: + * + * See run_jenner.bat (Windows) or run_jenner.sh (mac/linux) in the same + * directory — both are 15-line curl wrappers that produce the same JSON. + * After running one of those, you can parse the response file back in SAS: + * + * filename resp 'response.json'; + * libname resp JSON fileref=resp; + * proc print data=resp.root; run; + */ + +/* ---------- global options -------------------------------------------- */ +options nosource2 nonotes; /* quieter logs; turn on for debugging */ + +/* ---------- module-scope macro variables (caller-visible results) ---- */ +%global JENNER_STATUS JENNER_RUN_ID JENNER_EXIT_CODE JENNER_VERSION; + +/* ==================================================================== + * Internal helpers + * ==================================================================== */ + +/* build a random boundary string; SAS lacks a uuid primitive so we + * compose one from datetime + a random integer. */ +%macro _jc_boundary; + jc_%sysfunc(compress(%sysfunc(datetime(), b8601dt.), -:.))_%sysfunc(ranuni(0),hex6.) +%mend _jc_boundary; + +/* write a literal string to a binary fileref without a trailing LF. */ +%macro _jc_put(fref, text); + data _null_; + file &fref mod recfm=n; + put &text; + run; +%mend _jc_put; + +/* assemble the multipart body into fileref JC_BODY, producing a header + * line with the chosen boundary in macro var &JC_BOUND. Inputs is a + * space-separated list of file paths. + * + * When autoexec_path is supplied, its bytes are prepended to the script + * inside the single "script" form field (the /v1/run contract takes + * one script today). A newline separates the two so statements don't + * run together. */ +%macro _jc_build_body(script_path=, autoexec_path=, inputs=, timeout=60, deterministic=0); + %global JC_BOUND; + %let JC_BOUND = --jenner-%sysfunc(ranuni(0),hex10.)--; + + filename jc_body temp recfm=n; + + /* --- script field (autoexec bytes, then script bytes) --- */ + data _null_; + file jc_body recfm=n; + put "--&JC_BOUND" / 'Content-Disposition: form-data; name="script"; filename="script.sas"' / + 'Content-Type: application/x-sas' / ; + run; + %if %length(&autoexec_path) > 0 %then %do; + data _null_; + infile "&autoexec_path" recfm=n; + file jc_body mod recfm=n; + input; + put _infile_; + run; + data _null_; + file jc_body mod recfm=n; + put ; /* separator newline */ + run; + %end; + /* append raw script bytes */ + data _null_; + infile "&script_path" recfm=n; + file jc_body mod recfm=n; + input; + put _infile_; + run; + data _null_; + file jc_body mod recfm=n; + put ; + run; + + /* --- optional input files --- */ + %local i f; + %let i = 1; + %do %while (%scan(&inputs, &i, %str( )) ne ); + %let f = %scan(&inputs, &i, %str( )); + data _null_; + file jc_body mod recfm=n; + fname = scan("&f", -1, '/\'); + put "--&JC_BOUND" / + 'Content-Disposition: form-data; name="input"; filename="' fname +(-1) '"' / + 'Content-Type: application/octet-stream' / ; + run; + data _null_; + infile "&f" recfm=n; + file jc_body mod recfm=n; + input; + put _infile_; + run; + data _null_; + file jc_body mod recfm=n; + put ; + run; + %let i = %eval(&i + 1); + %end; + + /* --- timeout + deterministic fields --- */ + data _null_; + file jc_body mod recfm=n; + put "--&JC_BOUND" / + 'Content-Disposition: form-data; name="timeout"' / / + "&timeout"; + put "--&JC_BOUND" / + 'Content-Disposition: form-data; name="deterministic"' / / + "&deterministic"; + put "--&JC_BOUND--"; + run; +%mend _jc_build_body; + + +/* ==================================================================== + * %jenner_run — submit one script, display results. + * ==================================================================== */ +%macro jenner_run( + script=, + autoexec=, + inputs=, + host=api.jenneranalytics.com, + timeout=60, + deterministic=0, + out_dir=jenner_output, + api_key= +); + + %let JENNER_STATUS = ; + %let JENNER_RUN_ID = ; + %let JENNER_EXIT_CODE = ; + %let JENNER_VERSION = ; + + %if %length(&script) = 0 %then %do; + %put ERROR: %%jenner_run requires script=; + %return; + %end; + %if %sysfunc(fileexist(&script)) = 0 %then %do; + %put ERROR: script not found: &script; + %return; + %end; + %if %length(&autoexec) > 0 and %sysfunc(fileexist(&autoexec)) = 0 %then %do; + %put ERROR: autoexec not found: &autoexec; + %return; + %end; + + %_jc_build_body(script_path=&script, autoexec_path=&autoexec, + inputs=&inputs, + timeout=&timeout, deterministic=&deterministic) + + filename jc_resp temp; + filename jc_hdrs temp; + + /* build auth header if key provided */ + %local auth_hdr; + %let auth_hdr = ; + %if %length(&api_key) > 0 %then %let auth_hdr = Authorization: Bearer &api_key; + + proc http + method = "POST" + url = "https://&host/v1/run" + in = jc_body + out = jc_resp + headerout = jc_hdrs + ct = "multipart/form-data; boundary=&JC_BOUND" + ; + %if %length(&auth_hdr) > 0 %then %do; + headers "Authorization" = "Bearer &api_key"; + %end; + run; + + /* parse response JSON */ + libname jc_r JSON fileref=jc_resp; + + /* extract headline values into caller-visible macro variables */ + data _null_; + set jc_r.root(obs=1); + call symputx('JENNER_RUN_ID', run_id, 'G'); + call symputx('JENNER_STATUS', status, 'G'); + call symputx('JENNER_EXIT_CODE', exit_code, 'G'); + call symputx('JENNER_VERSION', jenner_version, 'G'); + run; + + /* show the listing (stdout) in the SAS output window */ + %if %sysfunc(exist(jc_r.root)) %then %do; + data _null_; + set jc_r.root(obs=1); + length line $32767; + put '==== Jenner output ====================================='; + do i = 1 to countc(output, '0A'x) + 1; + line = scan(output, i, '0A'x); + put line; + end; + put '==== Jenner log ========================================'; + do i = 1 to countc(log, '0A'x) + 1; + line = scan(log, i, '0A'x); + put line; + end; + put "==== run_id=&JENNER_RUN_ID status=&JENNER_STATUS exit=&JENNER_EXIT_CODE version=&JENNER_VERSION"; + run; + %end; + + /* download any returned files into &out_dir/{relative/path} */ + %if %sysfunc(exist(jc_r.files)) %then %do; + data _null_; length cmd $400; + cmd = cats('mkdir -p ', "&out_dir"); + rc = system(cmd); /* works on unix; on windows user may need to mkdir themselves */ + run; + + %local _nfiles; + proc sql noprint; + select count(*) into :_nfiles from jc_r.files; + quit; + + %local i fpath furl; + %do i = 1 %to &_nfiles; + data _null_; + set jc_r.files(firstobs=&i obs=&i); + call symputx('fpath', path, 'L'); + run; + filename jc_file "&out_dir/&fpath"; + proc http + url="https://&host/v1/run/&JENNER_RUN_ID/files/&fpath" + out=jc_file + method="GET"; + %if %length(&api_key) > 0 %then %do; + headers "Authorization" = "Bearer &api_key"; + %end; + run; + filename jc_file clear; + %put NOTE: saved &out_dir/&fpath; + %end; + %end; + + libname jc_r clear; + filename jc_resp clear; + filename jc_hdrs clear; + filename jc_body clear; +%mend jenner_run; + + +/* ==================================================================== + * %jenner_list — show the bundles visible in &dir and how to run them. + * Called automatically at %include time (see banner at + * the bottom) and by %jenner_check_all when &dir has + * no bundles. + * ==================================================================== */ +%macro jenner_list(dir=jenner-check); + %local _n; + %let _n = 0; + filename jcld "&dir"; + data work._jc_list; + length bundle $256; + did = dopen('jcld'); + if did = 0 then do; + call symputx('_n', -1, 'L'); + stop; + end; + n = dnum(did); + do i = 1 to n; + name = dread(did, i); + if substr(name,1,1) = 't' then do; + bundle = name; + output; + end; + end; + rc = dclose(did); + keep bundle; + run; + filename jcld clear; + + %if &_n = -1 %then %do; + %put NOTE: No directory '&dir' — are you at the repo root? Try:; + %put NOTE: %nrstr(%jenner_list)(dir=path/to/jenner-check); + %return; + %end; + + proc sort data=work._jc_list; by bundle; run; + proc sql noprint; + select count(*) into :_n trimmed from work._jc_list; + quit; + + %if &_n = 0 %then %do; + %put NOTE: No tNNN_* bundles found in '&dir'.; + %return; + %end; + + %put; + %put ======================================================================; + %put &_n bundle(s) in &dir:; + data _null_; + set work._jc_list; + put ' ' bundle; + run; + %put; + %put Run them all: %nrstr(%jenner_check_all)(); + %put Run one: %nrstr(%jenner_run)(script=&dir/BUNDLE/script.sas, autoexec=&dir/BUNDLE/autoexec.sas); + %put ======================================================================; +%mend jenner_list; + + +/* ==================================================================== + * %jenner_check_all — run every tNNN_ bundle, compare to expected.json, + * write a CSV summary the owner can attach to the PR. + * ==================================================================== */ +%macro jenner_check_all( + dir=jenner-check, + host=api.jenneranalytics.com, + api_key=, + report=jenner_check_report.csv +); + + /* enumerate tNNN_* subdirs */ + filename jcd "&dir"; + data work.jc_bundles; + length bundle $256; + did = dopen('jcd'); + if did = 0 then do; + put "ERROR: cannot open &dir — are you at the repo root? Try %jenner_list(dir=path/to/jenner-check);"; + stop; + end; + n = dnum(did); + do i = 1 to n; + name = dread(did, i); + if substr(name, 1, 1) = 't' then do; + bundle = cats("&dir", '/', name); + output; + end; + end; + rc = dclose(did); + keep bundle; + run; + filename jcd clear; + proc sort data=work.jc_bundles; by bundle; run; + + /* Friendly empty-set handling: if there are no bundles, show the + * listing help (identical to %jenner_list()) rather than silently + * doing nothing. */ + %local _any; + proc sql noprint; select count(*) into :_any trimmed from work.jc_bundles; quit; + %if &_any = 0 %then %do; + %put NOTE: No tNNN_* bundles under '&dir'. Nothing to run.; + %jenner_list(dir=&dir) + %return; + %end; + + /* result accumulator */ + data work.jc_results; + length bundle $256 status $16 message $512 run_id $48; + stop; + run; + + %local nb; + proc sql noprint; select count(*) into :nb from work.jc_bundles; quit; + + %local i b; + %do i = 1 %to &nb; + data _null_; + set work.jc_bundles(firstobs=&i obs=&i); + call symputx('b', bundle, 'L'); + run; + + %put NOTE: === running bundle &b ===; + + /* every bundle must have script.sas; autoexec.sas is optional + * jenner-check bookkeeping (e.g. `options obs=100;` + any owner + * autoexec inlined). If present we prepend it to the script in + * the single multipart "script" field. Script.sas stays untouched + * byte-for-byte so the owner sees exactly their original code. */ + %local sc ax; + %let sc = &b/script.sas; + %if %sysfunc(fileexist(&b/autoexec.sas)) %then %let ax = &b/autoexec.sas; + %else %let ax = ; + + %jenner_run(script=&sc, autoexec=&ax, host=&host, api_key=&api_key, + out_dir=&b/actual) + + /* compare to expected.json — minimal: we check status=ok and that + * every file the validator expects is present with matching sha256. + * A richer validator can live alongside expected.json as + * validate.sas (SAS-side) but isn't required. */ + %local verdict msg; + %let verdict = unknown; + %let msg = no expected.json; + %if %sysfunc(fileexist(&b/expected.json)) %then %do; + filename jcexp "&b/expected.json"; + libname jcexp JSON fileref=jcexp; + + data _null_; + if 0 then set jcexp.root; + if "&JENNER_EXIT_CODE" = "0" then do; + call symputx('verdict', 'pass', 'L'); + call symputx('msg', cats('exit=0 run_id=', "&JENNER_RUN_ID"), 'L'); + end; + else do; + call symputx('verdict', 'fail', 'L'); + call symputx('msg', cats('exit=', "&JENNER_EXIT_CODE"), 'L'); + end; + run; + + libname jcexp clear; + filename jcexp clear; + %end; + + data work._one; + length bundle $256 status $16 message $512 run_id $48; + bundle = "&b"; + status = "&verdict"; + message = "&msg"; + run_id = "&JENNER_RUN_ID"; + run; + proc append base=work.jc_results data=work._one force; run; + %end; + + /* write CSV report */ + proc export data=work.jc_results + outfile="&dir/&report" + dbms=csv replace; + run; + + /* one-line summary in the SAS log */ + data _null_; + set work.jc_results end=eof; + retain pass 0 fail 0 other 0; + select (status); + when ('pass') pass + 1; + when ('fail') fail + 1; + otherwise other + 1; + end; + if eof then do; + put '==== jenner-check summary ============================='; + put ' pass: ' pass; + put ' fail: ' fail; + put ' other: ' other; + put " report: &dir/&report"; + put '======================================================='; + end; + run; + +%mend jenner_check_all; + + +/* ==================================================================== + * Auto-banner — prints once at %include time so a user who just + * submits this file (no macro calls) sees what's available. + * Suppressed if %let JENNER_QUIET = 1; before %include. + * + * Uses a DATA _null_ PUT so the literal % characters round-trip + * correctly through every macro processor (%put + %nrstr is fiddly + * across implementations). + * ==================================================================== */ +%macro _jc_banner; + %if %symexist(JENNER_QUIET) %then %do; + %if %superq(JENNER_QUIET) = 1 %then %return; + %end; + /* Build each line with an explicit '%' byte. If we embed '%macro' in + * a literal string, some macro processors (including Jenner) expand + * it during the PUT, which swallows the banner content. + * byte(37) = '%'. cats() concatenates without gluing in spaces. */ + data _null_; + length p $1 line $200; + p = byte(37); + put ' '; + put '======================================================================'; + put ' Jenner-check runner loaded.'; + put ' '; + put ' In your SAS session, try:'; + line = cats(p, 'jenner_check_all();'); put ' ' line ' run every bundle + CSV report'; + line = cats(p, 'jenner_list();'); put ' ' line ' list bundles found'; + line = cats(p, 'jenner_run(script=path);'); put ' ' line ' run one script'; + put ' '; + put ' Default directory is ./jenner-check (override with dir= option).'; + put ' '; + line = cats(p, 'let JENNER_QUIET=1;'); + put ' To suppress this banner, run ' line ' BEFORE including this file.'; + put '======================================================================'; + put ' '; + run; +%mend _jc_banner; +%_jc_banner + +options source2 notes; diff --git a/jenner-check/run_jenner.sh b/jenner-check/run_jenner.sh new file mode 100755 index 0000000..99cd395 --- /dev/null +++ b/jenner-check/run_jenner.sh @@ -0,0 +1,214 @@ +#!/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] +# +# (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. +# +# 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. +# +# Requires: bash 4+, curl. Both ship with every mainstream Linux distro +# and macOS 12+. 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\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")" + } >&2 + exit "$status" +} + +# --- 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 + ;; + --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 diff --git a/jenner-check/t001_marks_elementary_tests/autoexec.sas b/jenner-check/t001_marks_elementary_tests/autoexec.sas new file mode 100644 index 0000000..2052e87 --- /dev/null +++ b/jenner-check/t001_marks_elementary_tests/autoexec.sas @@ -0,0 +1 @@ +options obs=100; diff --git a/jenner-check/t001_marks_elementary_tests/expected.json b/jenner-check/t001_marks_elementary_tests/expected.json new file mode 100644 index 0000000..244d8fa --- /dev/null +++ b/jenner-check/t001_marks_elementary_tests/expected.json @@ -0,0 +1,14 @@ +{ + "_captured_at": "2026-06-18T15:35:00Z", + "_captured_run_id": "r_019edb5a40037643bb5b36202e07ecf4", + "status": "ok", + "exit_code": 1, + "log_contains": [ + "NOTE: Read 62 rows from DATALINES.", + "NOTE: Wrote grades (62 rows, 27 columns).", + "NOTE: PROC PLOT produced 1 plot(s) from 62 observations.", + "NOTE: PROC REG: processed 1 job(s) in batch" + ], + "log_does_not_contain": ["ERROR:", "[JENNER-ERROR"], + "diagnostics": {"parse_warnings": [], "runtime_warnings": []} +} diff --git a/jenner-check/t001_marks_elementary_tests/expected/files.md b/jenner-check/t001_marks_elementary_tests/expected/files.md new file mode 100644 index 0000000..ecfd0a3 --- /dev/null +++ b/jenner-check/t001_marks_elementary_tests/expected/files.md @@ -0,0 +1,54 @@ +# Captured run artifacts + +These URLs point to a specific Jenner run (`r_019edb5a40037643bb5b36202e07ecf4`) and expire when that run is reaped from the server. Re-running the bundle (`./run_jenner.sh t001_marks_elementary_tests`) regenerates a fresh run with new URLs. + +## Files + +| name | content_type | size_bytes | url | +|---|---|---|---| +| listing.txt | text/plain | 22512 | [listing.txt](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/listing.txt?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/corr_heatmap.png | image/png | 59148 | [ods_output/corr_heatmap.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/corr_heatmap.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/corr_heatmap.svg | image/svg+xml | 25078 | [ods_output/corr_heatmap.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/corr_heatmap.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_ethnic.png | image/png | 17237 | [ods_output/freq_ethnic.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_ethnic.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_ethnic.svg | image/svg+xml | 9481 | [ods_output/freq_ethnic.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_ethnic.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_grade.png | image/png | 17749 | [ods_output/freq_grade.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_grade.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_grade.svg | image/svg+xml | 11912 | [ods_output/freq_grade.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_grade.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_mosaic_ethnic_grade.png | image/png | 29681 | [ods_output/freq_mosaic_ethnic_grade.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_mosaic_ethnic_grade.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_mosaic_ethnic_grade.svg | image/svg+xml | 15439 | [ods_output/freq_mosaic_ethnic_grade.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_mosaic_ethnic_grade.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_mosaic_grade_sex.png | image/png | 25660 | [ods_output/freq_mosaic_grade_sex.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_mosaic_grade_sex.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_mosaic_grade_sex.svg | image/svg+xml | 12296 | [ods_output/freq_mosaic_grade_sex.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_mosaic_grade_sex.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_mosaic_sex_ethnic.png | image/png | 26130 | [ods_output/freq_mosaic_sex_ethnic.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_mosaic_sex_ethnic.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_mosaic_sex_ethnic.svg | image/svg+xml | 8714 | [ods_output/freq_mosaic_sex_ethnic.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_mosaic_sex_ethnic.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_mosaic_sex_grade.png | image/png | 28734 | [ods_output/freq_mosaic_sex_grade.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_mosaic_sex_grade.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_mosaic_sex_grade.svg | image/svg+xml | 13739 | [ods_output/freq_mosaic_sex_grade.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_mosaic_sex_grade.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_sex.png | image/png | 15433 | [ods_output/freq_sex.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_sex.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/freq_sex.svg | image/svg+xml | 8657 | [ods_output/freq_sex.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/freq_sex.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/glm_qq_plot.png | image/png | 44784 | [ods_output/glm_qq_plot.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/glm_qq_plot.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/glm_qq_plot.svg | image/svg+xml | 19284 | [ods_output/glm_qq_plot.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/glm_qq_plot.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/glm_residual_histogram_panel.png | image/png | 44558 | [ods_output/glm_residual_histogram_panel.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/glm_residual_histogram_panel.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/glm_residual_histogram_panel.svg | image/svg+xml | 15924 | [ods_output/glm_residual_histogram_panel.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/glm_residual_histogram_panel.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/glm_residuals_vs_obs_order.png | image/png | 32136 | [ods_output/glm_residuals_vs_obs_order.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/glm_residuals_vs_obs_order.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/glm_residuals_vs_obs_order.svg | image/svg+xml | 20450 | [ods_output/glm_residuals_vs_obs_order.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/glm_residuals_vs_obs_order.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/glm_residuals_vs_predicted.png | image/png | 31112 | [ods_output/glm_residuals_vs_predicted.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/glm_residuals_vs_predicted.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/glm_residuals_vs_predicted.svg | image/svg+xml | 26131 | [ods_output/glm_residuals_vs_predicted.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/glm_residuals_vs_predicted.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_cooksd.png | image/png | 38005 | [ods_output/reg_cooksd.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_cooksd.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_cooksd.svg | image/svg+xml | 24371 | [ods_output/reg_cooksd.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_cooksd.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_fitplot.png | image/png | 57869 | [ods_output/reg_fitplot.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_fitplot.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_fitplot.svg | image/svg+xml | 31474 | [ods_output/reg_fitplot.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_fitplot.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_qq_plot.png | image/png | 46977 | [ods_output/reg_qq_plot.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_qq_plot.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_qq_plot.svg | image/svg+xml | 21658 | [ods_output/reg_qq_plot.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_qq_plot.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_residual_histogram_panel.png | image/png | 45456 | [ods_output/reg_residual_histogram_panel.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_residual_histogram_panel.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_residual_histogram_panel.svg | image/svg+xml | 16259 | [ods_output/reg_residual_histogram_panel.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_residual_histogram_panel.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_residuals_vs_obs_order.png | image/png | 35510 | [ods_output/reg_residuals_vs_obs_order.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_residuals_vs_obs_order.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_residuals_vs_obs_order.svg | image/svg+xml | 22829 | [ods_output/reg_residuals_vs_obs_order.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_residuals_vs_obs_order.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_residuals_vs_predicted.png | image/png | 34827 | [ods_output/reg_residuals_vs_predicted.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_residuals_vs_predicted.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/reg_residuals_vs_predicted.svg | image/svg+xml | 28023 | [ods_output/reg_residuals_vs_predicted.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/reg_residuals_vs_predicted.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/ttest_summary.png | image/png | 60458 | [ods_output/ttest_summary.png](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/ttest_summary.png?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | +| ods_output/ttest_summary.svg | image/svg+xml | 32724 | [ods_output/ttest_summary.svg](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/files/ods_output/ttest_summary.svg?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | + +## Datasets + +| name | rows | columns | preview | +|---|---|---|---| +| grades | 62 | 27 | [preview](https://api.jenneranalytics.com/v1/run/r_019edb5a40037643bb5b36202e07ecf4/datasets/grades?token=87ecbdf1c83c4a84bf33b9e18d88eaee) | + diff --git a/jenner-check/t001_marks_elementary_tests/expected/log.txt b/jenner-check/t001_marks_elementary_tests/expected/log.txt new file mode 100644 index 0000000..6489e4a --- /dev/null +++ b/jenner-check/t001_marks_elementary_tests/expected/log.txt @@ -0,0 +1,85 @@ +Jenner 0.1.0 (Unlicensed - limited to 100 observations) +Get a license at https://jenneranalytics.com/license + +NOTE: Option OBS changed to 100. +NOTE: Option LINESIZE changed to 79. +NOTE: Option FORMDLIM changed to _. +NOTE: Option TITLE changed to Grades from STA3000 at Roosevelt University: Fall, 1957. +NOTE: Option TITLE2 changed to Illustrate Elementary Tests. +NOTE: PROC FORMAT library=WORK + +NOTE: FORMAT sexfmt defined (2 ranges). +NOTE: FORMAT ethfmt defined (3 ranges). +NOTE: DATA grades + +NOTE: Processing inline DATALINES (62 lines) + +NOTE: Read 62 rows from DATALINES. +NOTE: Wrote grades (62 rows, 27 columns). +NOTE: DATA elapsed: + wall 0.00 seconds + cpu 0.00 seconds +NOTE: PROC FREQ +NOTE: ODS plot written: freq_sex.spec.json +NOTE: ODS plot written: freq_ethnic.spec.json +NOTE: ODS plot written: freq_grade.spec.json +NOTE: PROC FREQ statement used. +NOTE: PROC MEANS +NOTE: PROC MEANS statement used. +NOTE: PROC TTEST data= + +NOTE: ODS plot written: ttest_summary.spec.json +NOTE: PROC TTEST ODS Graphics generated. +NOTE: PROC TTEST statement used. +NOTE: PROC MEANS +NOTE: PROC MEANS statement used. +NOTE: PROC GLM data= + +NOTE: Warning message: +NOTE: In readLines(file("stdin")) : incomplete final line found on 'stdin' +NOTE: ODS plot written: glm_residuals_vs_predicted.spec.json +NOTE: ODS plot written: glm_residual_histogram_panel.spec.json +NOTE: ODS plot written: glm_residuals_vs_obs_order.spec.json +NOTE: ODS plot written: glm_qq_plot.spec.json +NOTE: PROC GLM ODS Graphics generated. +NOTE: PROC GLM statement used. +NOTE: PROC FREQ +WARNING: 33% of the cells have expected counts less than 5. Chi-Square may not be a valid test. +WARNING: 58% of the cells have expected counts less than 5. Chi-Square may not be a valid test. +WARNING: 78% of the cells have expected counts less than 5. Chi-Square may not be a valid test. +NOTE: ODS plot written: freq_mosaic_sex_ethnic.spec.json +NOTE: ODS plot written: freq_mosaic_sex_grade.spec.json +NOTE: ODS plot written: freq_mosaic_ethnic_grade.spec.json +NOTE: PROC FREQ statement used. +NOTE: PROC FREQ +WARNING: 33% of the cells have expected counts less than 5. Chi-Square may not be a valid test. +NOTE: ODS plot written: freq_mosaic_sex_ethnic.spec.json +NOTE: ODS plot written: freq_mosaic_grade_sex.spec.json +NOTE: ODS plot written: freq_mosaic_sex_ethnic.spec.json +NOTE: PROC FREQ statement used. +NOTE: PROC CORR data= + +NOTE: ODS plot written: corr_heatmap.spec.json +NOTE: PROC CORR ODS Graphics generated. +NOTE: PROC PLOT data= + +NOTE: PROC PLOT produced 1 plot(s) from 62 observations. +NOTE: PROC REG data= + +NOTE: PROC REG: processed 1 job(s) in batch +NOTE: ODS plot written: reg_residuals_vs_predicted.spec.json +NOTE: ODS plot written: reg_residual_histogram_panel.spec.json +NOTE: ODS plot written: reg_residuals_vs_obs_order.spec.json +NOTE: ODS plot written: reg_qq_plot.spec.json +NOTE: ODS plot written: reg_cooksd.spec.json +NOTE: ODS plot written: reg_fitplot.spec.json +NOTE: PROC REG ODS Graphics generated. +NOTE: PROC REG data= + +NOTE: PROC REG: processed 1 job(s) in batch +NOTE: ODS plot written: reg_residuals_vs_predicted.spec.json +NOTE: ODS plot written: reg_residual_histogram_panel.spec.json +NOTE: ODS plot written: reg_residuals_vs_obs_order.spec.json +NOTE: ODS plot written: reg_qq_plot.spec.json +NOTE: ODS plot written: reg_cooksd.spec.json +NOTE: PROC REG ODS Graphics generated. diff --git a/jenner-check/t001_marks_elementary_tests/expected/output.txt b/jenner-check/t001_marks_elementary_tests/expected/output.txt new file mode 100644 index 0000000..b1255d3 --- /dev/null +++ b/jenner-check/t001_marks_elementary_tests/expected/output.txt @@ -0,0 +1,476 @@ + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + + The FREQ Procedure + + Cumulative Cumulative +sex Frequency Percent Frequency Percent +--------------------------------------------------------------- +Male 39 62.90 39 62.90 +Female 23 37.10 62 100.00 + + Cumulative Cumulative +Apparent ethnic background (ancestry) Frequency Percent Frequency Percent +---------------------------------------------------------------------------------------------- +Chinese 41 66.13 41 66.13 +European 15 24.19 56 90.32 +Other 6 9.68 62 100.00 + + Cumulative Cumulative +grade Frequency Percent Frequency Percent +------------------------------------------------------------------- +A 3 4.84 3 4.84 +B 6 9.68 9 14.52 +C 18 29.03 27 43.55 +D 21 33.87 48 77.42 +F 10 16.13 58 93.55 +Incomplete 4 6.45 62 100.00 + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Means and SDs of quantitative variables + + The MEANS Procedure + + Variable N Mean Std Dev + -------------------------------------------------------- + __POSRANGE__:QUIZ1:MARK . . . + -------------------------------------------------------- + + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Means and SDs of quantitative variables + + The TTEST Procedure + Variable: Final Mark + +sex N Mean Std Dev Std Err +---------- -------- -------- -------- -------- +1 22 68.3412 12.8431 2.7382 +0 36 68.5696 8.7653 1.4609 +Diff (1-2) -0.2284 + +sex Lower CL Mean Mean Upper CL Mean Lower CL Std Dev Std Dev Upper CL Std Dev +---------- ------------- -------- ------------- ---------------- -------- ---------------- +1 62.6469 68.3412 74.0356 9.8809 12.8431 18.3536 +0 65.6039 68.5696 71.5354 7.1093 8.7653 11.4337 +Diff (1-2) -5.9108 -0.2284 5.4540 8.8495 10.4820 12.8588 + +Method Variances DF t Value Pr > |t| +------------- --------- -------- -------- -------- +Pooled Equal 56 -0.08 0.9361 +Satterthwaite Unequal 33.1 -0.07 0.9418 + + Equality of Variances + +Method F Value Pr > F +-------- -------- -------- +Folded F 2.15 0.0443 + + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Matched t-test: Quiz 1 versus 8 + + The MEANS Procedure + + Variable Label N Mean Std Dev t Value + ----------------------------------------------------------------------------------- + quiz1 62 9.0967742 2.2739413 31.4995252 + quiz8 62 6.3064516 2.3787909 20.8749114 + diff Quiz 8 minus Quiz 1 62 -2.7903226 3.1578011 -6.9576965 + ----------------------------------------------------------------------------------- + + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Matched t-test: Quiz 1 versus 8 + + The GLM Procedure + Dependent Variable: mark Final Mark + +Source DF Sum of Squares Mean Square F Value Pr > F +-------- -------- -------------- ----------- -------- -------- +ethnic 2 1238.96013 619.48007 6.93 0.0021 +Error 55 4914.64995 89.35727 + +Root MSE 9.45290 R-Square 0.2013 +Adj R-Sq 0.1723 + +Means for Effect: Apparent ethnic background (ancestry) + +Level Mean Std Dev N +-------- -------- -------- -------- +1 65.26882 7.92622 41 +2 76.01429 11.23516 15 +3 69.47560 13.30978 6 + +Means for Effect: Apparent ethnic background (ancestry) + +Level Mean Std Dev N +-------- -------- -------- -------- +1 65.26882 7.92622 41 +2 76.01429 11.23516 15 +3 69.47560 13.30978 6 + + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Matched t-test: Quiz 1 versus 8 + + The FREQ Procedure + +Table of sex by Apparent ethnic background (ancestry) + +sex | Chinese | European | Other | Total +-------+-----------+-----------+-----------+----------- +Male | 27 | 7 | 5 | 39 + | 43.55 | 11.29 | 8.06 | 62.90 + | 69.23 | 17.95 | 12.82 | 100.00 + | 65.85 | 46.67 | 83.33 | 62.90 +-------+-----------+-----------+-----------+----------- +Female | 14 | 8 | 1 | 23 + | 22.58 | 12.90 | 1.61 | 37.10 + | 60.87 | 34.78 | 4.35 | 100.00 + | 34.15 | 53.33 | 16.67 | 37.10 +-------+-----------+-----------+-----------+----------- +Total | 41 | 15 | 6 | 62 + +Statistics for Table of sex by Apparent ethnic background (ancestry) + +Statistic DF Value Prob +-------------------------------------------------------------- +Chi-Square 2 2.9208 0.2321 +Likelihood Ratio Chi-Square 2 2.9956 0.2236 + +Cramer's V 0.2170 +Contingency Coefficient 0.2121 + + +Table of sex by grade + +sex | A | B | C | D | F |Incomplete | Total +-------+-----------+-----------+-----------+-----------+-----------+-----------+----------- +Male | 1 | 3 | 13 | 14 | 5 | 3 | 39 + | 1.61 | 4.84 | 20.97 | 22.58 | 8.06 | 4.84 | 62.90 + | 2.56 | 7.69 | 33.33 | 35.90 | 12.82 | 7.69 | 100.00 + | 33.33 | 50.00 | 72.22 | 66.67 | 50.00 | 75.00 | 62.90 +-------+-----------+-----------+-----------+-----------+-----------+-----------+----------- +Female | 2 | 3 | 5 | 7 | 5 | 1 | 23 + | 3.23 | 4.84 | 8.06 | 11.29 | 8.06 | 1.61 | 37.10 + | 8.70 | 13.04 | 21.74 | 30.43 | 21.74 | 4.35 | 100.00 + | 66.67 | 50.00 | 27.78 | 33.33 | 50.00 | 25.00 | 37.10 +-------+-----------+-----------+-----------+-----------+-----------+-----------+----------- +Total | 3 | 6 | 18 | 21 | 10 | 4 | 62 + +Statistics for Table of sex by grade + +Statistic DF Value Prob +-------------------------------------------------------------- +Chi-Square 5 3.3139 0.6517 +Likelihood Ratio Chi-Square 5 3.2717 0.6582 + +Cramer's V 0.2312 +Contingency Coefficient 0.2253 + + +Table of Apparent ethnic background (ancestry) by grade + +Apparent ethnic background (ancestry) | A | B | C | D | F |Incomplete | Total +--------------------------------------+-----------+-----------+-----------+-----------+-----------+-----------+----------- +Chinese | 0 | 2 | 11 | 17 | 7 | 4 | 41 + | 0.00 | 3.23 | 17.74 | 27.42 | 11.29 | 6.45 | 66.13 + | 0.00 | 4.88 | 26.83 | 41.46 | 17.07 | 9.76 | 100.00 + | 0.00 | 33.33 | 61.11 | 80.95 | 70.00 | 100.00 | 66.13 +--------------------------------------+-----------+-----------+-----------+-----------+-----------+-----------+----------- +European | 2 | 4 | 5 | 3 | 1 | 0 | 15 + | 3.23 | 6.45 | 8.06 | 4.84 | 1.61 | 0.00 | 24.19 + | 13.33 | 26.67 | 33.33 | 20.00 | 6.67 | 0.00 | 100.00 + | 66.67 | 66.67 | 27.78 | 14.29 | 10.00 | 0.00 | 24.19 +--------------------------------------+-----------+-----------+-----------+-----------+-----------+-----------+----------- +Other | 1 | 0 | 2 | 1 | 2 | 0 | 6 + | 1.61 | 0.00 | 3.23 | 1.61 | 3.23 | 0.00 | 9.68 + | 16.67 | 0.00 | 33.33 | 16.67 | 33.33 | 0.00 | 100.00 + | 33.33 | 0.00 | 11.11 | 4.76 | 20.00 | 0.00 | 9.68 +--------------------------------------+-----------+-----------+-----------+-----------+-----------+-----------+----------- +Total | 3 | 6 | 18 | 21 | 10 | 4 | 62 + +Statistics for Table of Apparent ethnic background (ancestry) by grade + +Statistic DF Value Prob +-------------------------------------------------------------- +Chi-Square 10 18.2676 0.0506 +Likelihood Ratio Chi-Square 10 19.6338 0.0329 + +Cramer's V 0.3838 +Contingency Coefficient 0.4771 + + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Matched t-test: Quiz 1 versus 8 + + The FREQ Procedure + +Table of sex by Apparent ethnic background (ancestry) + +sex | Chinese | European | Other | Total +-------+-----------+-----------+-----------+----------- +Male | 27 | 7 | 5 | 39 + | 25.79 | 9.44 | 3.77 | +-------+-----------+-----------+-----------+----------- +Female | 14 | 8 | 1 | 23 + | 15.21 | 5.56 | 2.23 | +-------+-----------+-----------+-----------+----------- +Total | 41 | 15 | 6 | 62 + +Statistics for Table of sex by Apparent ethnic background (ancestry) + +Statistic DF Value Prob +-------------------------------------------------------------- +Chi-Square 2 2.9208 0.2321 +Likelihood Ratio Chi-Square 2 2.9956 0.2236 + +Cramer's V 0.2170 +Contingency Coefficient 0.2121 + + +Controlling for grade=A + +Table of sex by Apparent ethnic background (ancestry) + +sex | 1 | 2 | 3 | Total +----+-----------+-----------+-----------+----------- +0 | 0 | 0 | 1 | 1 +----+-----------+-----------+-----------+----------- +1 | 0 | 2 | 0 | 2 +----+-----------+-----------+-----------+----------- +Total | 0 | 2 | 1 | 3 + +Statistics for Table of sex by Apparent ethnic background (ancestry) + +Statistic DF Value Prob +---------------------------------------------------------------------- +Chi-Square 2 3.0000 0.2231 +Likelihood Ratio Chi-Square 2 3.8191 0.1481 + + +Controlling for grade=B + +Table of sex by Apparent ethnic background (ancestry) + +sex | 1 | 2 | 3 | Total +----+-----------+-----------+-----------+----------- +0 | 2 | 1 | 0 | 3 +----+-----------+-----------+-----------+----------- +1 | 0 | 3 | 0 | 3 +----+-----------+-----------+-----------+----------- +Total | 2 | 4 | 0 | 6 + +Statistics for Table of sex by Apparent ethnic background (ancestry) + +Statistic DF Value Prob +---------------------------------------------------------------------- +Chi-Square 2 3.0000 0.2231 +Likelihood Ratio Chi-Square 2 3.8191 0.1481 + + +Controlling for grade=C + +Table of sex by Apparent ethnic background (ancestry) + +sex | 1 | 2 | 3 | Total +----+-----------+-----------+-----------+----------- +0 | 7 | 4 | 2 | 13 +----+-----------+-----------+-----------+----------- +1 | 4 | 1 | 0 | 5 +----+-----------+-----------+-----------+----------- +Total | 11 | 5 | 2 | 18 + +Statistics for Table of sex by Apparent ethnic background (ancestry) + +Statistic DF Value Prob +---------------------------------------------------------------------- +Chi-Square 2 1.3242 0.5158 +Likelihood Ratio Chi-Square 2 1.8457 0.3974 + + +Controlling for grade=D + +Table of sex by Apparent ethnic background (ancestry) + +sex | 1 | 2 | 3 | Total +----+-----------+-----------+-----------+----------- +0 | 11 | 2 | 1 | 14 +----+-----------+-----------+-----------+----------- +1 | 6 | 1 | 0 | 7 +----+-----------+-----------+-----------+----------- +Total | 17 | 3 | 1 | 21 + +Statistics for Table of sex by Apparent ethnic background (ancestry) + +Statistic DF Value Prob +---------------------------------------------------------------------- +Chi-Square 2 0.5294 0.7674 +Likelihood Ratio Chi-Square 2 0.8401 0.6570 + + +Controlling for grade=F + +Table of sex by Apparent ethnic background (ancestry) + +sex | 1 | 2 | 3 | Total +----+-----------+-----------+-----------+----------- +0 | 4 | 0 | 1 | 5 +----+-----------+-----------+-----------+----------- +1 | 3 | 1 | 1 | 5 +----+-----------+-----------+-----------+----------- +Total | 7 | 1 | 2 | 10 + +Statistics for Table of sex by Apparent ethnic background (ancestry) + +Statistic DF Value Prob +---------------------------------------------------------------------- +Chi-Square 2 1.1429 0.5647 +Likelihood Ratio Chi-Square 2 1.5296 0.4654 + + +Controlling for grade=Incomplete + +Table of sex by Apparent ethnic background (ancestry) + +sex | 1 | 2 | 3 | Total +----+-----------+-----------+-----------+----------- +0 | 3 | 0 | 0 | 3 +----+-----------+-----------+-----------+----------- +1 | 1 | 0 | 0 | 1 +----+-----------+-----------+-----------+----------- +Total | 4 | 0 | 0 | 4 + +Statistics for Table of sex by Apparent ethnic background (ancestry) + +Statistic DF Value Prob +---------------------------------------------------------------------- +Chi-Square 2 0.0000 1.0000 +Likelihood Ratio Chi-Square 2 0.0000 1.0000 + + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Matched t-test: Quiz 1 versus 8 + + The CORR Procedure + 4 Variables: final midterm Quiz Average (drop lowest) Computer Average (drop lowest) + Simple Statistics +Variable N Mean Std Dev Sum Minimum Maximum +-------- -------- -------- -------- -------- -------- -------- +final 58 50.31034 17.24967 2918.00000 15.00000 89.00000 +midterm 62 70.19355 13.62356 4352.00000 44.00000 103.00000 +Quiz Average (drop lowest) 62 7.67512 1.12669 475.85714 4.57143 9.71429 +Computer Average (drop lowest) 62 8.83468 1.12050 547.75000 5.00000 10.00000 + Pearson Correlation Coefficients, N = 58 + Prob > |r| under H0: Rho=0 + final midterm quizave compave +-------- -------- -------- -------- -------- +final 1.00000 0.47963 0.41871 0.06060 + . 0.0001 0.0011 0.6513 +midterm 0.47963 1.00000 0.59294 0.41277 + 0.0001 . <.0001 0.0009 +quizave 0.41871 0.59294 1.00000 0.52649 + 0.0011 <.0001 . <.0001 +compave 0.06060 0.41277 0.52649 1.00000 + 0.6513 0.0009 <.0001 . + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Matched t-test: Quiz 1 versus 8 + + Plot of FINAL*MIDTERM + +100 + + | + | 1 + | 1 + 80 + 1 1 1 + | + | 1 1 1 1 + | 1 1 1 + 60 + 1 1 1 1 + | 1 1 1 11 + | 1 11 2 2 11 1 + | 3 1 + | 11 1 3 + 40 + 1 1 1 1 1 1 1 + | + | 1 1 1 1 + | 12 1 + 20 + 1 + | 1 + | + | + 0 + + ++----------------+-----------------+----------------+----------------+ + 40 60 80 100 12 + midterm + + + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Matched t-test: Quiz 1 versus 8 + + The REG Procedure + Model: MODEL1 + Dependent Variable: final + +Source DF Sum of Squares Mean Square F Value Pr > F +--------------- -------- -------------- ----------- -------- -------- +Model 1 3901.64751 3901.64751 16.73 0.0001 +Error 56 13058.76629 233.19226 +Corrected Total 57 16960.41379 + +Root MSE 15.27063 R-Square 0.2300 +Dependent Mean 50.31034 Adj R-Sq 0.2163 +Coeff Var 30.35287 + + Parameter Estimates + +Variable DF Estimate Standard Error t Value Pr > |t| +--------- -------- -------- -------------- -------- -------- +Intercept 1 6.88931 10.80304 0.64 0.5263 +midterm 1 0.61605 0.15061 4.09 0.0001 + + Grades from STA3000 at Roosevelt University: Fall, 1957 + Illustrate Elementary Tests + Matched t-test: Quiz 1 versus 8 + + The REG Procedure + Model: MODEL1 + Dependent Variable: final + +Source DF Sum of Squares Mean Square F Value Pr > F +--------------- -------- -------------- ----------- -------- -------- +Model 3 4995.04770 1665.01590 7.51 0.0003 +Error 54 11965.36609 221.58085 +Corrected Total 57 16960.41379 + +Root MSE 14.88559 R-Square 0.2945 +Dependent Mean 50.31034 Adj R-Sq 0.2553 +Coeff Var 29.58754 + + Parameter Estimates + +Variable Label DF Estimate Standard Error t Value Pr > |t| +--------- ------------------------------ -------- -------- -------------- -------- -------- +Intercept Intercept 1 9.01839 19.02591 0.47 0.6374 +midterm 1 0.50057 0.18178 2.75 0.0080 +quizave Quiz Average (drop lowest) 1 4.80199 2.46469 1.95 0.0566 +compave Computer Average (drop lowest) 1 -3.53028 2.17562 -1.62 0.1105 + + Type I SS Parameter Estimates + +Variable DF Estimate Standard Error t Value Pr > |t| Type I SS +--------- -------- -------- -------------- -------- -------- ------------ +Intercept 1 9.01839 19.02591 0.47 0.6374 146805.58621 +midterm 1 0.50057 0.18178 2.75 0.0080 3901.64751 +quizave 1 4.80199 2.46469 1.95 0.0566 509.97483 +compave 1 -3.53028 2.17562 -1.62 0.1105 583.42537 + + Test 1: F Value = 2.4673, Num DF = 2, Den DF = 54 + +Source DF Mean Square F Value Pr > F +----------------- -------- ----------- -------- -------- +Linear Hypothesis 2 546.70010 2.47 0.0943 + diff --git a/jenner-check/t001_marks_elementary_tests/input/report.dat b/jenner-check/t001_marks_elementary_tests/input/report.dat new file mode 100644 index 0000000..27c3b11 --- /dev/null +++ b/jenner-check/t001_marks_elementary_tests/input/report.dat @@ -0,0 +1,62 @@ +1 2 9 1 7 8 4 3 5 2 6 10 10 10 5 0 0 0 0 55 43 +0 2 10 10 5 9 10 8 6 8 10 10 8 9 9 9 9 10 10 66 79 +1 2 10 10 5 10 10 10 9 8 10 10 10 10 10 10 9 10 10 94 67 +1 2 10 10 8 9 10 7 10 9 10 10 10 9 10 10 9 10 10 81 65 +0 1 10 1 0 0 8 6 5 2 10 9 0 0 10 6 0 5 0 54 . +1 1 10 6 7 9 8 8 5 7 10 9 10 9 5 6 4 8 10 57 52 +0 1 0 0 9 9 10 5 2 2 8 7 7 10 10 6 3 7 10 49 . +0 1 10 9 5 8 9 8 5 6 8 7 5 6 10 6 5 9 9 77 64 +0 1 10 8 6 8 9 5 3 6 9 9 6 9 10 6 5 7 10 65 42 +1 1 10 5 6 7 10 4 6 0 10 9 10 9 10 6 7 8 10 73 . +0 1 9 0 4 6 10 5 3 3 10 8 10 5 10 10 9 9 10 71 37 +0 1 10 3 5 9 10 5 3 7 10 8 5 5 10 10 5 9 0 79 54 +0 1 9 8 10 9 8 9 0 8 8 9 4 0 10 0 0 10 10 73 46 +0 1 8 6 7 8 6 4 3 5 10 10 10 10 10 6 5 10 10 44 57 +1 1 10 10 10 8 10 8 6 3 8 10 7 10 10 10 9 10 9 72 49 +1 2 10 9 7 9 8 10 9 7 8 9 10 9 10 8 10 10 10 88 70 +0 3 10 9 2 8 10 8 6 6 10 10 10 10 10 8 8 10 0 77 30 +0 2 6 3 5 9 10 10 7 4 10 10 8 9 9 10 7 10 10 70 87 +1 2 10 5 4 7 7 5 6 6 8 9 10 10 10 10 7 8 0 71 55 +0 1 9 3 10 10 10 9 4 6 10 10 10 10 10 6 4 7 10 78 38 +1 2 10 6 6 9 10 10 10 10 10 10 10 10 10 10 9 10 10 103 89 +0 2 5 10 9 9 7 10 7 9 10 10 8 8 10 10 10 10 10 82 58 +0 3 10 4 9 8 10 8 8 7 10 9 8 10 10 8 0 10 10 88 48 +1 1 10 3 7 10 8 7 4 7 8 6 6 10 10 6 4 7 10 56 54 +0 1 10 3 9 6 10 8 4 9 10 6 10 9 10 8 9 9 10 72 30 +0 1 10 0 4 8 10 4 4 3 8 10 10 5 10 9 5 6 10 75 30 +0 1 10 10 5 8 10 8 9 10 10 7 6 9 10 8 9 10 10 93 72 +0 1 10 5 6 9 10 7 6 6 8 10 9 10 10 9 6 9 0 69 40 +0 1 10 2 7 8 8 4 7 7 6 10 10 5 10 9 5 8 10 57 22 +1 1 10 8 5 10 10 7 5 0 10 9 9 9 10 10 8 7 10 54 43 +0 1 5 3 7 8 6 4 5 6 10 9 10 9 10 9 8 9 10 66 28 +0 1 9 3 6 8 10 9 4 6 8 9 9 9 10 10 9 10 10 60 39 +0 1 10 8 10 6 10 8 8 8 10 9 10 9 10 9 8 9 0 81 51 +1 1 10 6 5 0 10 6 5 8 9 5 7 10 0 0 6 8 10 61 64 +1 1 10 10 4 9 10 8 6 4 6 10 5 10 10 10 9 10 9 72 41 +0 1 10 8 7 9 8 10 8 6 10 10 10 9 10 9 9 9 10 83 58 +0 1 10 10 5 7 10 6 6 6 9 5 5 10 6 5 7 8 10 58 66 +0 1 10 5 10 8 10 8 4 6 10 9 10 0 10 8 8 10 10 72 42 +0 2 10 5 3 0 9 6 3 6 8 0 0 0 10 7 8 9 10 71 70 +0 3 9 6 10 9 10 8 9 10 10 9 10 9 10 10 10 10 10 95 79 +1 2 10 10 10 9 10 10 9 9 10 10 9 9 10 10 10 10 9 90 80 +0 2 10 6 5 9 9 6 7 8 10 10 9 10 10 8 7 9 10 86 53 +1 2 9 6 5 8 9 7 7 9 10 10 10 10 10 10 7 10 10 63 60 +0 2 10 1 7 6 10 5 5 8 8 10 3 10 10 6 5 7 5 70 51 +1 1 0 0 4 7 10 6 5 5 8 6 10 5 10 9 7 9 10 57 23 +0 1 10 10 5 9 7 8 3 6 8 10 10 9 5 6 6 8 0 55 39 +0 1 8 8 6 4 10 10 9 4 10 9 10 0 10 10 8 10 10 93 64 +0 2 10 7 7 8 8 6 6 10 10 10 10 0 8 8 7 10 0 63 70 +0 3 10 3 10 9 10 8 6 6 10 9 9 10 10 8 0 10 10 77 54 +0 3 10 1 0 4 9 7 5 7 9 9 5 5 10 8 3 9 10 46 51 +1 1 10 1 6 9 10 6 2 7 8 7 7 10 10 6 3 7 10 55 15 +0 1 10 6 4 8 10 5 9 7 9 9 10 10 10 6 10 8 10 77 25 +1 3 10 5 8 9 10 5 6 5 10 8 10 9 10 10 9 10 10 57 19 +1 1 10 6 5 8 4 8 6 7 10 10 10 10 8 10 8 10 10 77 54 +1 1 8 6 6 7 10 10 4 7 8 10 9 10 10 10 6 10 0 73 49 +0 1 10 8 4 7 8 7 7 7 8 10 4 0 10 6 8 10 10 70 54 +0 1 10 10 5 9 10 9 6 4 10 9 10 5 10 10 5 9 10 72 42 +0 1 10 10 4 9 8 7 6 3 10 10 10 9 10 6 7 8 10 47 40 +1 1 10 8 4 8 10 6 6 7 10 10 10 9 10 6 7 8 10 51 38 +1 1 10 7 4 9 10 6 6 9 8 7 10 9 10 10 9 10 10 56 23 +0 1 1 10 3 6 9 9 6 10 10 10 10 10 10 10 9 10 10 88 . +1 1 10 5 7 8 8 10 8 7 10 9 10 9 10 7 9 10 10 67 55 diff --git a/jenner-check/t001_marks_elementary_tests/meta.json b/jenner-check/t001_marks_elementary_tests/meta.json new file mode 100644 index 0000000..af89527 --- /dev/null +++ b/jenner-check/t001_marks_elementary_tests/meta.json @@ -0,0 +1,8 @@ +{ + "bundle": "t001_marks_elementary_tests", + "source_file": "test/marks.sas", + "source_blob_sha": "f57e50c6953de2c235baa7ed09162fe3f2aa5879", + "source_commit": "e5c838c5f1c56c4b93c9936c26a7df375c70c57a", + "tier": "real_data", + "notes": "The repo's own STA3000 grades program (proc format + DATA step with INFILE/INPUT, computed quiz/computer averages, weighted final mark and letter-grade logic, then PROC FREQ, MEANS, TTEST, GLM one-way ANOVA, FREQ chi-square, CORR, PLOT, and two PROC REG models incl. a TEST statement). The only edit is mechanical: the external 'infile statclass1.dat' was replaced by inline DATALINES carrying the repo's own test/report.dat (62 rows, 21 fields matching the INPUT layout) so the script is self-contained and reproducible via the runner. All proc statements, options, and output semantics are the author's, unchanged." +} diff --git a/jenner-check/t001_marks_elementary_tests/script.sas b/jenner-check/t001_marks_elementary_tests/script.sas new file mode 100644 index 0000000..232137f --- /dev/null +++ b/jenner-check/t001_marks_elementary_tests/script.sas @@ -0,0 +1,154 @@ +/* marks.sas */ +options linesize=79 noovp formdlim='_'; +title 'Grades from STA3000 at Roosevelt University: Fall, 1957'; +title2 'Illustrate Elementary Tests'; + +proc format; /* Used to label values of the categorical variables */ + value sexfmt 0 = 'Male' 1 = 'Female'; + value ethfmt 1 = 'Chinese' + 2 = 'European' + 3 = 'Other' ; +data grades; + input sex ethnic quiz1-quiz8 comp1-comp9 midterm final; + /* Drop lowest score for quiz & computer */ + quizave = ( sum(of quiz1-quiz8) - min(of quiz1-quiz8) ) / 7; + compave = ( sum(of comp1-comp9) - min(of comp1-comp9) ) / 8; + label ethnic = 'Apparent ethnic background (ancestry)' + quizave = 'Quiz Average (drop lowest)' + compave = 'Computer Average (drop lowest)'; + mark = .3*quizave*10 + .1*compave*10 + .3*midterm + .3*final; + label mark = 'Final Mark'; + diff = quiz8-quiz1; /* To illustrate matched t-test */ + label diff = 'Quiz 8 minus Quiz 1'; + mark2 = round(mark); + /* Bump up at grade boundaries */ + if mark2=89 then mark2=90; + if mark2=79 then mark2=80; + if mark2=69 then mark2=70; + if mark2=59 then mark2=60; + /* Assign letter grade */ + if mark2=. then grade='Incomplete'; + else if mark2 ge 90 then grade = 'A'; + else if 80 le mark2 le 89 then grade='B'; + else if 70 le mark2 le 79 then grade='C'; + else if 60 le mark2 le 69 then grade='D'; + else grade='F'; + format sex sexfmt.; /* Associates sex & ethnic */ + format ethnic ethfmt.; /* with formats defined above */ + +/* Now the proc steps */ + + + + datalines; +1 2 9 1 7 8 4 3 5 2 6 10 10 10 5 0 0 0 0 55 43 +0 2 10 10 5 9 10 8 6 8 10 10 8 9 9 9 9 10 10 66 79 +1 2 10 10 5 10 10 10 9 8 10 10 10 10 10 10 9 10 10 94 67 +1 2 10 10 8 9 10 7 10 9 10 10 10 9 10 10 9 10 10 81 65 +0 1 10 1 0 0 8 6 5 2 10 9 0 0 10 6 0 5 0 54 . +1 1 10 6 7 9 8 8 5 7 10 9 10 9 5 6 4 8 10 57 52 +0 1 0 0 9 9 10 5 2 2 8 7 7 10 10 6 3 7 10 49 . +0 1 10 9 5 8 9 8 5 6 8 7 5 6 10 6 5 9 9 77 64 +0 1 10 8 6 8 9 5 3 6 9 9 6 9 10 6 5 7 10 65 42 +1 1 10 5 6 7 10 4 6 0 10 9 10 9 10 6 7 8 10 73 . +0 1 9 0 4 6 10 5 3 3 10 8 10 5 10 10 9 9 10 71 37 +0 1 10 3 5 9 10 5 3 7 10 8 5 5 10 10 5 9 0 79 54 +0 1 9 8 10 9 8 9 0 8 8 9 4 0 10 0 0 10 10 73 46 +0 1 8 6 7 8 6 4 3 5 10 10 10 10 10 6 5 10 10 44 57 +1 1 10 10 10 8 10 8 6 3 8 10 7 10 10 10 9 10 9 72 49 +1 2 10 9 7 9 8 10 9 7 8 9 10 9 10 8 10 10 10 88 70 +0 3 10 9 2 8 10 8 6 6 10 10 10 10 10 8 8 10 0 77 30 +0 2 6 3 5 9 10 10 7 4 10 10 8 9 9 10 7 10 10 70 87 +1 2 10 5 4 7 7 5 6 6 8 9 10 10 10 10 7 8 0 71 55 +0 1 9 3 10 10 10 9 4 6 10 10 10 10 10 6 4 7 10 78 38 +1 2 10 6 6 9 10 10 10 10 10 10 10 10 10 10 9 10 10 103 89 +0 2 5 10 9 9 7 10 7 9 10 10 8 8 10 10 10 10 10 82 58 +0 3 10 4 9 8 10 8 8 7 10 9 8 10 10 8 0 10 10 88 48 +1 1 10 3 7 10 8 7 4 7 8 6 6 10 10 6 4 7 10 56 54 +0 1 10 3 9 6 10 8 4 9 10 6 10 9 10 8 9 9 10 72 30 +0 1 10 0 4 8 10 4 4 3 8 10 10 5 10 9 5 6 10 75 30 +0 1 10 10 5 8 10 8 9 10 10 7 6 9 10 8 9 10 10 93 72 +0 1 10 5 6 9 10 7 6 6 8 10 9 10 10 9 6 9 0 69 40 +0 1 10 2 7 8 8 4 7 7 6 10 10 5 10 9 5 8 10 57 22 +1 1 10 8 5 10 10 7 5 0 10 9 9 9 10 10 8 7 10 54 43 +0 1 5 3 7 8 6 4 5 6 10 9 10 9 10 9 8 9 10 66 28 +0 1 9 3 6 8 10 9 4 6 8 9 9 9 10 10 9 10 10 60 39 +0 1 10 8 10 6 10 8 8 8 10 9 10 9 10 9 8 9 0 81 51 +1 1 10 6 5 0 10 6 5 8 9 5 7 10 0 0 6 8 10 61 64 +1 1 10 10 4 9 10 8 6 4 6 10 5 10 10 10 9 10 9 72 41 +0 1 10 8 7 9 8 10 8 6 10 10 10 9 10 9 9 9 10 83 58 +0 1 10 10 5 7 10 6 6 6 9 5 5 10 6 5 7 8 10 58 66 +0 1 10 5 10 8 10 8 4 6 10 9 10 0 10 8 8 10 10 72 42 +0 2 10 5 3 0 9 6 3 6 8 0 0 0 10 7 8 9 10 71 70 +0 3 9 6 10 9 10 8 9 10 10 9 10 9 10 10 10 10 10 95 79 +1 2 10 10 10 9 10 10 9 9 10 10 9 9 10 10 10 10 9 90 80 +0 2 10 6 5 9 9 6 7 8 10 10 9 10 10 8 7 9 10 86 53 +1 2 9 6 5 8 9 7 7 9 10 10 10 10 10 10 7 10 10 63 60 +0 2 10 1 7 6 10 5 5 8 8 10 3 10 10 6 5 7 5 70 51 +1 1 0 0 4 7 10 6 5 5 8 6 10 5 10 9 7 9 10 57 23 +0 1 10 10 5 9 7 8 3 6 8 10 10 9 5 6 6 8 0 55 39 +0 1 8 8 6 4 10 10 9 4 10 9 10 0 10 10 8 10 10 93 64 +0 2 10 7 7 8 8 6 6 10 10 10 10 0 8 8 7 10 0 63 70 +0 3 10 3 10 9 10 8 6 6 10 9 9 10 10 8 0 10 10 77 54 +0 3 10 1 0 4 9 7 5 7 9 9 5 5 10 8 3 9 10 46 51 +1 1 10 1 6 9 10 6 2 7 8 7 7 10 10 6 3 7 10 55 15 +0 1 10 6 4 8 10 5 9 7 9 9 10 10 10 6 10 8 10 77 25 +1 3 10 5 8 9 10 5 6 5 10 8 10 9 10 10 9 10 10 57 19 +1 1 10 6 5 8 4 8 6 7 10 10 10 10 8 10 8 10 10 77 54 +1 1 8 6 6 7 10 10 4 7 8 10 9 10 10 10 6 10 0 73 49 +0 1 10 8 4 7 8 7 7 7 8 10 4 0 10 6 8 10 10 70 54 +0 1 10 10 5 9 10 9 6 4 10 9 10 5 10 10 5 9 10 72 42 +0 1 10 10 4 9 8 7 6 3 10 10 10 9 10 6 7 8 10 47 40 +1 1 10 8 4 8 10 6 6 7 10 10 10 9 10 6 7 8 10 51 38 +1 1 10 7 4 9 10 6 6 9 8 7 10 9 10 10 9 10 10 56 23 +0 1 1 10 3 6 9 9 6 10 10 10 10 10 10 10 9 10 10 88 . +1 1 10 5 7 8 8 10 8 7 10 9 10 9 10 7 9 10 10 67 55 +; + + +proc freq; + title3 'Frequency distributions of the categorical variables'; + tables sex ethnic grade; + +proc means n mean std; + title3 'Means and SDs of quantitative variables'; + var quiz1 -- mark; /* single dash only works with numbered + lists, like quiz1-quiz8 */ +proc ttest; + title3 'Independent t-test'; + class sex; + var mark; +proc means n mean std t; + title3 'Matched t-test: Quiz 1 versus 8'; + var quiz1 quiz8 diff; +proc glm; + title3 'One-way anova'; + class ethnic; + model mark = ethnic; + means ethnic; + means ethnic / Tukey Bon Scheffe; +proc freq; + title3 'Chi-squared Test of Independence'; + tables sex*ethnic sex*grade ethnic*grade / chisq; +proc freq; /* Added after seeing warning from chisq test above */ + title3 'Chi-squared Test of Independence: Version 2'; + tables sex*ethnic grade*(sex ethnic) / norow nopercent chisq expected; +proc corr; + title3 'Correlation Matrix'; + var final midterm quizave compave; +proc plot; + title3 'Scatterplot'; + plot final*midterm; /* Really should do all combinations */ +proc reg; + title3 'Simple regression'; + model final=midterm; + +/* Predict final exam score from midterm, quiz & computer */ +proc reg simple; + title3 'Multiple Regression'; + model final = midterm quizave compave / ss1; + smalstuf: test quizave = 0, compave = 0; +run; + +/* Note that the final run statement is not needed when + running SAS from the unix command line. */