As a clinical trial statistician or programmer, answer three short questions in Your review: the task, the inputs and outputs, and the AI-assisted review. Agreeing with all three approves this benchmark; disagreeing with any needs a comment. Propose a fix with the Edit button on any source file, or add it to your comments.
We suggest spending no more than 5 minutes on a review. The other tabs beside Your review are background for assessment, not under review.
Summary
README.mdPublic entries: Python and R. Passing coverage requires an installed inventory.
Goal: build one record per test from the wide vital signs
form collected at each visit, carrying VSTESTCD,
VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC,
VSSTRESU, VSPOS, VSSTAT, VSREASND, and VSLOC, plus
the visit name in VISIT and the measurement date in VSDTC.
Input: long-form Operational Data Model (ODM) rows, one row per collected form item, with the collected entry in the value field. Each visit contributes one vitals form group: the visit date, body position, temperature location, each measurement with its unit, and the blood pressure not-done flag with its reason, each as its own item.
Variables:
VSTESTCDis the test code:SYSBP,DIABP,PULSE,RESP,TEMP, orWEIGHT.VSTESTis the test name:Systolic Blood Pressure,Diastolic Blood Pressure,Pulse Rate,Respiratory Rate,Temperature, orWeight.VSORRESis the result exactly as collected on the form; blank when the measurement was not done.VSORRESUis the unit exactly as collected:mmHg,beats/min,breaths/min,C, orkg; blank when no result was collected.VSSTRESNis the numeric result in the standard unit; the form already collects standard units, so it equals the collected result; missing when no result was collected.VSSTRESCis the same standardized value written as text, so it always agrees with the numeric result; blank when no result was collected.VSSTRESUis the standard unit, equal to the collected unit; blank when no result was collected.VSPOSis the body position as collected on the form (SITTING) on blood pressure and pulse records with a collected result; blank otherwise.VSSTATisNOT DONEwhen the form flags blood pressure as not done; blank otherwise.VSREASNDis the reason the form gives for blood pressure not done; blank otherwise.VSLOCis the body location of the measurement as collected on the form (ORAL CAVITY) on temperature records with a collected result; blank otherwise.
Note: each visit's form group fans out into one record per test.
VSSEQ numbers them per subject by visit date, then in test order
within each visit. A not-done blood pressure still produces its two records
so the reason is kept, but they carry no result, unit, position,
or standardized value.
Inputs
Under review2 source filesinput/odm.csv
Edit58 rows| StudyOID | MetaDataVersionOID | SubjectKey | StudyEventOID | StudyEventRepeatKey | FormOID | FormRepeatKey | ItemGroupOID | ItemGroupRepeatKey | ItemOID | Value |
|---|---|---|---|---|---|---|---|---|---|---|
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.VSDTC | 2026-01-05 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.POSITION | SITTING |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPLOC | ORAL CAVITY |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBP | 118 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABP | 76 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSE | 68 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSEU | beats/min |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESP | 14 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESPU | breaths/min |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMP | 36.6 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPU | C |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHT | 70.2 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHTU | kg |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.VSDTC | 2026-02-02 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.POSITION | SITTING |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPLOC | ORAL CAVITY |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSE | 72 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSEU | beats/min |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESP | 16 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESPU | breaths/min |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMP | 36.8 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPU | C |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHT | 70.5 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHTU | kg |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.BPNOTDONE | Y |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.BPREASND | SUBJECT DECLINED |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.VSDTC | 2026-01-06 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.POSITION | SITTING |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPLOC | ORAL CAVITY |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBP | 124 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABP | 80 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSE | 71 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSEU | beats/min |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESP | 15 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESPU | breaths/min |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMP | 36.9 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPU | C |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHT | 82.4 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHTU | kg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.VSDTC | 2026-02-03 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.POSITION | SITTING |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPLOC | ORAL CAVITY |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBP | 120 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABP | 78 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSE | 69 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSEU | beats/min |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESP | 14 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESPU | breaths/min |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMP | 36.7 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPU | C |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHT | 81.6 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHTU | kg |
input/vs_tests.csv
Edit6 rows| VSTESTCD | VSTEST | TESTORD | HAS_POSITION | HAS_LOCATION | HAS_NOTDONE |
|---|---|---|---|---|---|
| SYSBP | Systolic Blood Pressure | 1 | 1 | 0 | 1 |
| DIABP | Diastolic Blood Pressure | 2 | 1 | 0 | 1 |
| PULSE | Pulse Rate | 3 | 1 | 0 | 0 |
| RESP | Respiratory Rate | 4 | 0 | 0 | 0 |
| TEMP | Temperature | 5 | 0 | 1 | 0 |
| WEIGHT | Weight | 6 | 0 | 0 | 0 |
Expected output
Under review24 expected rowsexpected/vs.csv
Edit24 rows| DOMAIN | STUDYID | USUBJID | VSSEQ | VISIT | VSTESTCD | VSTEST | VSORRES | VSORRESU | VSSTRESN | VSSTRESC | VSSTRESU | VSPOS | VSSTAT | VSREASND | VSLOC | VSDTC |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| VS | YAMAA-01 | 1 | SCREENING | SYSBP | Systolic Blood Pressure | 118 | mmHg | 118 | 118 | mmHg | SITTING | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 2 | SCREENING | DIABP | Diastolic Blood Pressure | 76 | mmHg | 76 | 76 | mmHg | SITTING | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 3 | SCREENING | PULSE | Pulse Rate | 68 | beats/min | 68 | 68 | beats/min | SITTING | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 4 | SCREENING | RESP | Respiratory Rate | 14 | breaths/min | 14 | 14 | breaths/min | — | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 5 | SCREENING | TEMP | Temperature | 36.6 | C | 36.6 | 36.6 | C | — | — | — | ORAL CAVITY | 2026-01-05 | |
| VS | YAMAA-01 | 6 | SCREENING | WEIGHT | Weight | 70.2 | kg | 70.2 | 70.2 | kg | — | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 7 | WEEK 4 | SYSBP | Systolic Blood Pressure | — | — | — | — | — | — | NOT DONE | SUBJECT DECLINED | — | 2026-02-02 | |
| VS | YAMAA-01 | 8 | WEEK 4 | DIABP | Diastolic Blood Pressure | — | — | — | — | — | — | NOT DONE | SUBJECT DECLINED | — | 2026-02-02 | |
| VS | YAMAA-01 | 9 | WEEK 4 | PULSE | Pulse Rate | 72 | beats/min | 72 | 72 | beats/min | SITTING | — | — | — | 2026-02-02 | |
| VS | YAMAA-01 | 10 | WEEK 4 | RESP | Respiratory Rate | 16 | breaths/min | 16 | 16 | breaths/min | — | — | — | — | 2026-02-02 | |
| VS | YAMAA-01 | 11 | WEEK 4 | TEMP | Temperature | 36.8 | C | 36.8 | 36.8 | C | — | — | — | ORAL CAVITY | 2026-02-02 | |
| VS | YAMAA-01 | 12 | WEEK 4 | WEIGHT | Weight | 70.5 | kg | 70.5 | 70.5 | kg | — | — | — | — | 2026-02-02 | |
| VS | YAMAA-01 | 1 | SCREENING | SYSBP | Systolic Blood Pressure | 124 | mmHg | 124 | 124 | mmHg | SITTING | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 2 | SCREENING | DIABP | Diastolic Blood Pressure | 80 | mmHg | 80 | 80 | mmHg | SITTING | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 3 | SCREENING | PULSE | Pulse Rate | 71 | beats/min | 71 | 71 | beats/min | SITTING | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 4 | SCREENING | RESP | Respiratory Rate | 15 | breaths/min | 15 | 15 | breaths/min | — | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 5 | SCREENING | TEMP | Temperature | 36.9 | C | 36.9 | 36.9 | C | — | — | — | ORAL CAVITY | 2026-01-06 | |
| VS | YAMAA-01 | 6 | SCREENING | WEIGHT | Weight | 82.4 | kg | 82.4 | 82.4 | kg | — | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 7 | WEEK 4 | SYSBP | Systolic Blood Pressure | 120 | mmHg | 120 | 120 | mmHg | SITTING | — | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 8 | WEEK 4 | DIABP | Diastolic Blood Pressure | 78 | mmHg | 78 | 78 | mmHg | SITTING | — | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 9 | WEEK 4 | PULSE | Pulse Rate | 69 | beats/min | 69 | 69 | beats/min | SITTING | — | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 10 | WEEK 4 | RESP | Respiratory Rate | 14 | breaths/min | 14 | 14 | breaths/min | — | — | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 11 | WEEK 4 | TEMP | Temperature | 36.7 | C | 36.7 | 36.7 | C | — | — | — | ORAL CAVITY | 2026-02-03 | |
| VS | YAMAA-01 | 12 | WEEK 4 | WEIGHT | Weight | 81.6 | kg | 81.6 | 81.6 | kg | — | — | — | — | 2026-02-03 |
Human review and assessment
| Variable Name | Variable Label | Type | Length | Controlled Terms or Format | Origin | Core | Conversion Definition | Variable Type | Variable Order | Comments for Define |
|---|---|---|---|---|---|---|---|---|---|---|
| DOMAIN | Domain Abbreviation | Char | Assigned | Set constant value "VS". | SDTM | 1 | ||||
| STUDYID | Study Identifier | Char | Collected | Copy value from ODM.StudyOID. | SDTM | 2 | ||||
| USUBJID | Unique Subject Identifier | Char | Collected | Copy value from ODM.SubjectKey. | SDTM | 3 | ||||
| VISIT | Visit Name | Char | Collected | Copy value from ODM.StudyEventOID. | SDTM | 4 | ||||
| VSDTC | Date/Time of Measurements | Char | ISO 8601 | Collected | Read collected ODM item IT.VS.VSDTC; no identified record yields blank. | SDTM | 5 | |||
| VSTESTCD | Vital Signs Test Short Name | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 6 | ||||
| VSTEST | Vital Signs Test Name | Char | Derived | Take VSTEST from the TESTS record matching on VSTESTCD, and a row with no match is an error. | SDTM | 7 | ||||
| VSORRES | Result or Finding in Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 8 | ||||
| VSORRESU | Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 9 | ||||
| VSSTRESN | Numeric Result/Finding in Standard Units | Num | Derived | Copy value from VSORRES; a value that cannot be converted → blank. | SDTM | 10 | ||||
| VSSTRESC | Character Result/Finding in Std Format | Char | Derived | Copy value from VSSTRESN. | SDTM | 11 | ||||
| VSSTRESU | Standard Units | Char | Derived | Copy value from VSORRESU. | SDTM | 12 | ||||
| VSPOS | Position of Subject | Char | Derived | If HAS_POSITION = 1 AND VSORRES IS NOT NULL then "POSITION"; otherwise blank. | SDTM | 13 | ||||
| VSLOC | Location of Vital Signs Measurement | Char | Derived | If HAS_LOCATION = 1 AND VSORRES IS NOT NULL then "TEMPLOC"; otherwise blank. | SDTM | 14 | ||||
| VSSTAT | Completion Status | Char | Derived | If HAS_NOTDONE = 1 AND BPNOTDONE = 'Y' then "NOT DONE"; otherwise blank. | SDTM | 15 | ||||
| VSREASND | Reason Not Done | Char | Derived | If HAS_NOTDONE = 1 AND BPNOTDONE = 'Y' then "BPREASND"; otherwise blank. | SDTM | 16 | ||||
| VSSEQ | Sequence Number | Num | Derived | Row number within each group of STUDYID, USUBJID, ordered by VSDTC then TESTORD. | SDTM | 17 |
| Template | Include when | VSTESTCD | VSORRES | VSORRESU |
|---|---|---|---|---|
| sysbp | Constant "SYSBP" | Read collected ODM item IT.VS.SYSBP; no identified record yields blank | Read collected ODM item IT.VS.SYSBPU; no identified record yields blank | |
| diabp | Constant "DIABP" | Read collected ODM item IT.VS.DIABP; no identified record yields blank | Read collected ODM item IT.VS.DIABPU; no identified record yields blank | |
| pulse | Constant "PULSE" | Read collected ODM item IT.VS.PULSE; no identified record yields blank | Read collected ODM item IT.VS.PULSEU; no identified record yields blank | |
| resp | Constant "RESP" | Read collected ODM item IT.VS.RESP; no identified record yields blank | Read collected ODM item IT.VS.RESPU; no identified record yields blank | |
| temp | Constant "TEMP" | Read collected ODM item IT.VS.TEMP; no identified record yields blank | Read collected ODM item IT.VS.TEMPU; no identified record yields blank | |
| weight | Constant "WEIGHT" | Read collected ODM item IT.VS.WEIGHT; no identified record yields blank | Read collected ODM item IT.VS.WEIGHTU; no identified record yields blank |
| Version | Date | Author | Description | Reviewer | Sign-off |
|---|---|---|---|---|---|
| 1.7 | 2026-10-20 | yamaa docs build | Read a specification in the 1.0 surface as its 0.5 counterpart; state that missing values stay missing where a mapping declares no missing handler, and leave out a declared missing answer that restates a handler's default | ||
| 1.6 | 2026-10-17 | yamaa docs build | Describe collected date parts written as ISO 8601 date text and times of day counted in seconds since midnight | ||
| 1.5 | 2026-10-17 | yamaa docs build | Name collected precision in words instead of letter codes; state imputation fallbacks, minimum source precision, bounds ignored outside the collected period, and invalid text with no handler as an error; describe date and datetime shifts, imputation flag codes, and partial text giving no date part | ||
| 1.4 | 2026-10-15 | yamaa docs build | State datetime precision codes through the hour and minute, and complete only the time parts a collected datetime lacks | ||
| 1.3 | 2026-09-29 | yamaa docs build | Show study codelist names, external dictionaries, and ISO 8601 formats in variable rows; list the document's codelist values and metadata | ||
| 1.2 | 2026-09-29 | yamaa docs build | Classify ADaM copies from a declared input as Predecessor and recodes as Derived, and SDTM recodes of locally derived columns as Derived | ||
| 1.1 | 2026-09-29 | yamaa docs build | Resolve lookups into the donor record they select, and give every derivation shape a plain-language rule instead of its raw YAML | ||
| 1.0 | yamaa docs build | Initial generation from spec.yaml |
prompts/sdtm-vs-collected-form/full.md
Edit41 linesFollowing CDISC SDTM standards, use the provided ODM and VS_TESTS datasets to create a VS dataset with one record per test from the vital signs form collected at each visit.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSSTAT, VSREASND, VSLOC, VSDTC
DOMAIN is "VS". STUDYID is the ODM study OID and USUBJID is the ODM subject key, each as given. VISIT is the name of the visit at which the form was collected, as given in ODM. VSDTC is the visit date collected on the form. Each visit's form group fans out into one record per test. VSTESTCD is "SYSBP", "DIABP", "PULSE", "RESP", "TEMP", or "WEIGHT". VSTEST is the test name VS_TESTS gives for the test code: "Systolic Blood Pressure", "Diastolic Blood Pressure", "Pulse Rate", "Respiratory Rate", "Temperature", or "Weight". VSORRES is the result exactly as collected on the form, and has no value when the measurement was not done. VSORRESU is the unit exactly as collected: "mmHg" for the blood pressure tests, "beats/min" for pulse, "breaths/min" for respiratory rate, "C" for temperature, and "kg" for weight; it has no value when no result was collected. VSSTRESN is the numeric result in the standard unit; the form already collects standard units, so it equals the collected result, and it has no value when no result was collected. VSSTRESC is the same standardized value written as text, so it always agrees with the numeric result, and it has no value when no result was collected. VSSTRESU is the standard unit, equal to the collected unit, and it has no value when no result was collected. VSPOS is the body position as collected on the form on blood pressure and pulse records with a collected result, and has no value otherwise. VSSTAT is "NOT DONE" on the blood pressure records when the form flags blood pressure as not done, and has no value otherwise. VSREASND is the reason the form gives for blood pressure not done, on the blood pressure records, and has no value otherwise. VSLOC is the body location of the measurement as collected on the form on temperature records with a collected result, and has no value otherwise. A not-done blood pressure still gives its two records, so the reason is kept, but those records carry no result, unit, position, or standardized value. VSSEQ numbers each subject's records from 1 by visit date, then in test order within each visit.
Read the source datasets from /app/input and save the completed dataset as /app/output/vs.csv.
prompts/sdtm-vs-collected-form/conventions.md
Edit16 linesFollowing CDISC SDTM standards, use the provided ODM and VS_TESTS datasets to create a VS dataset with one record per test from the vital signs form collected at each visit.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSSTAT, VSREASND, VSLOC, VSDTC
DOMAIN is "VS". VSTESTCD is "SYSBP", "DIABP", "PULSE", "RESP", "TEMP", or "WEIGHT". VSTEST is the test name VS_TESTS gives for the test code: "Systolic Blood Pressure", "Diastolic Blood Pressure", "Pulse Rate", "Respiratory Rate", "Temperature", or "Weight". VSSTAT is "NOT DONE" or has no value.
Read the source datasets from /app/input and save the completed dataset as /app/output/vs.csv.
prompts/sdtm-vs-collected-form/brief.md
Edit10 linesFollowing CDISC SDTM standards, use the provided ODM and VS_TESTS datasets to create a VS dataset with one record per test from the vital signs form collected at each visit.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSSTAT, VSREASND, VSLOC, VSDTC
Read the source datasets from /app/input and save the completed dataset as /app/output/vs.csv.
R
Edit3 lineslibrary(yamaa)yamaa_spec("spec.yaml")$run()$outputPython
Edit3 linesimport yamaayamaa.spec("spec.yaml").run().outputsolutions/sdtm-vs-collected-form/result.R
Edit113 lines# Reference solution for the yamaa benchmark sdtm-vs-collected-form (R track).## Written from the benchmark's full prompt and its inputs alone, as an agent# would write it. Harbor's oracle agent runs it to check that the prompt can# be solved and that the grader scores a correct answer 1.library(dplyr, warn.conflicts = FALSE)library(purrr)library(readr)library(tidyr)fmt_text <- function(x) { if (is.na(x)) { return(NA_character_) } if (x == floor(x)) { formatC(x, format = "f", digits = 0) } else { format(x, scientific = FALSE, trim = TRUE, digits = 15) }}odm <- read_csv( "/app/input/odm.csv", col_types = cols(.default = col_character()))tests <- read_csv( "/app/input/vs_tests.csv", col_types = cols(TESTORD = col_integer(), .default = col_character()))wide <- odm |> select(StudyOID, SubjectKey, StudyEventOID, ItemOID, Value) |> pivot_wider(names_from = ItemOID, values_from = Value, values_fn = first)rows <- list()for (i in seq_len(nrow(wide))) { visit <- wide[i, ] vsdtc <- visit$IT.VS.VSDTC position <- visit$IT.VS.POSITION location <- visit$IT.VS.TEMPLOC not_done <- !is.na(visit$IT.VS.BPNOTDONE) && visit$IT.VS.BPNOTDONE == "Y" reason <- visit$IT.VS.BPREASND for (j in seq_len(nrow(tests))) { test <- tests[j, ] testcd <- test$VSTESTCD result_item <- paste0("IT.VS.", testcd) unit_item <- paste0("IT.VS.", testcd, "U") result <- if (result_item %in% names(visit)) visit[[result_item]] else NA_character_ unit <- if (unit_item %in% names(visit)) visit[[unit_item]] else NA_character_ has_result <- !is.na(result) && result != "" if (testcd %in% c("SYSBP", "DIABP") && not_done) { has_result <- FALSE } if (!has_result) { orres <- NA_character_ orresu <- NA_character_ stresn <- NA_real_ stresc <- NA_character_ stresu <- NA_character_ pos <- NA_character_ loc <- NA_character_ } else { orres <- result orresu <- unit stresn <- suppressWarnings(as.numeric(result)) stresc <- fmt_text(stresn) stresu <- unit pos <- if (test$HAS_POSITION == "1") position else NA_character_ loc <- if (test$HAS_LOCATION == "1") location else NA_character_ } if (test$HAS_NOTDONE == "1" && not_done) { stat <- "NOT DONE" reasnd <- reason } else { stat <- NA_character_ reasnd <- NA_character_ } rows[[length(rows) + 1]] <- tibble( STUDYID = visit$StudyOID, USUBJID = visit$SubjectKey, VISIT = visit$StudyEventOID, VSTESTCD = testcd, VSTEST = test$VSTEST, TESTORD = test$TESTORD, VSORRES = orres, VSORRESU = orresu, VSSTRESN = stresn, VSSTRESC = stresc, VSSTRESU = stresu, VSPOS = pos, VSSTAT = stat, VSREASND = reasnd, VSLOC = loc, VSDTC = vsdtc ) }}vs <- bind_rows(rows) |> arrange(USUBJID, VSDTC, TESTORD) |> group_by(USUBJID) |> mutate(VSSEQ = row_number()) |> ungroup() |> arrange(USUBJID, VSSEQ) |> transmute( DOMAIN = "VS", STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSSTAT, VSREASND, VSLOC, VSDTC )dir.create("/app/output", showWarnings = FALSE)write_csv(vs, "/app/output/vs.csv", na = "")solutions/sdtm-vs-collected-form/result.py
Edit130 lines# Reference solution for the yamaa benchmark sdtm-vs-collected-form (Python track).## Written from the benchmark's full prompt and its inputs alone, as an agent# would write it. Harbor's oracle agent runs it to check that the prompt can# be solved and that the grader scores a correct answer 1."""Build the SDTM VS dataset from the collected vital signs form (ODM)and the VS_TESTS test list.One record per test per visit form. Reads /app/input, writes/app/output/vs.csv."""import csvimport osfrom collections import OrderedDictINPUT_DIR = "/app/input"OUTPUT_DIR = "/app/output"COLUMNS = [ "DOMAIN", "STUDYID", "USUBJID", "VSSEQ", "VISIT", "VSTESTCD", "VSTEST", "VSORRES", "VSORRESU", "VSSTRESN", "VSSTRESC", "VSSTRESU", "VSPOS", "VSSTAT", "VSREASND", "VSLOC", "VSDTC",]BP_TESTS = {"SYSBP", "DIABP"}POSITION_TESTS = {"SYSBP", "DIABP", "PULSE"}LOCATION_TESTS = {"TEMP"}def num_text(value): """Write a collected numeric result as text: integers without a decimal point, other values in their plain decimal form.""" number = float(value) if number == int(number): return str(int(number)) return repr(number)def main(): with open(os.path.join(INPUT_DIR, "vs_tests.csv"), newline="") as fh: tests = sorted( csv.DictReader(fh), key=lambda row: int(row["TESTORD"]) ) with open(os.path.join(INPUT_DIR, "odm.csv"), newline="") as fh: odm_rows = list(csv.DictReader(fh)) # Group the form items by subject and visit form occurrence, # keeping first-seen order. forms = OrderedDict() for row in odm_rows: key = ( row["StudyOID"], row["SubjectKey"], row["StudyEventOID"], row["StudyEventRepeatKey"], row["FormOID"], row["FormRepeatKey"], row["ItemGroupOID"], row["ItemGroupRepeatKey"], ) form = forms.setdefault(key, {}) item = row["ItemOID"].rsplit(".", 1)[-1] form[item] = row["Value"] # Collect one entry per subject: list of (visit date, test records). subjects = OrderedDict() for (studyid, subjid, visit, *_rest), form in forms.items(): vsdtc = form.get("VSDTC", "") position = form.get("POSITION", "") location = form.get("TEMPLOC", "") bp_not_done = form.get("BPNOTDONE", "") == "Y" bp_reason = form.get("BPREASND", "") if bp_not_done else "" records = [] for test in tests: code = test["VSTESTCD"] result = form.get(code, "") unit = form.get(code + "U", "") if result else "" if result: stresn = num_text(result) stresc = stresn else: stresn = "" stresc = "" records.append({ "DOMAIN": "VS", "STUDYID": studyid, "USUBJID": subjid, "VISIT": visit, "VSTESTCD": code, "VSTEST": test["VSTEST"], "VSORRES": result, "VSORRESU": unit, "VSSTRESN": stresn, "VSSTRESC": stresc, "VSSTRESU": unit, "VSPOS": position if (code in POSITION_TESTS and result) else "", "VSSTAT": "NOT DONE" if (code in BP_TESTS and bp_not_done) else "", "VSREASND": bp_reason if code in BP_TESTS else "", "VSLOC": location if (code in LOCATION_TESTS and result) else "", "VSDTC": vsdtc, }) subjects.setdefault(subjid, []).append((vsdtc, records)) out_rows = [] for subjid, visits in subjects.items(): visits.sort(key=lambda entry: entry[0]) seq = 0 for _vsdtc, records in visits: for record in records: seq += 1 record["VSSEQ"] = seq out_rows.append(record) os.makedirs(OUTPUT_DIR, exist_ok=True) with open(os.path.join(OUTPUT_DIR, "vs.csv"), "w", newline="") as fh: writer = csv.DictWriter(fh, fieldnames=COLUMNS) writer.writeheader() for record in out_rows: writer.writerow({col: record[col] for col in COLUMNS})if __name__ == "__main__": main()solutions/sdtm-vs-collected-form/result.sas
Edit50 lines/* opensas reference solution for the yamaa benchmark sdtm-vs-collected-form. Reads the benchmark inputs and derives the requested output. Execute with the existing opensas runtime: opensas /app/output/result.sas. */data odm; length StudyOID MetaDataVersionOID SubjectKey StudyEventOID StudyEventRepeatKey FormOID FormRepeatKey ItemGroupOID ItemGroupRepeatKey ItemOID Value $1024; infile "/app/input/odm.csv" dsd dlm="," firstobs=2 truncover lrecl=32767; input StudyOID : $char1024. MetaDataVersionOID : $char1024. SubjectKey : $char1024. StudyEventOID : $char1024. StudyEventRepeatKey : $char1024. FormOID : $char1024. FormRepeatKey : $char1024. ItemGroupOID : $char1024. ItemGroupRepeatKey : $char1024. ItemOID : $char1024. Value : $char1024.;run;data vs_tests; length VSTESTCD VSTEST TESTORD HAS_POSITION HAS_LOCATION HAS_NOTDONE $1024; infile "/app/input/vs_tests.csv" dsd dlm="," firstobs=2 truncover lrecl=32767; input VSTESTCD : $char1024. VSTEST : $char1024. TESTORD : $char1024. HAS_POSITION : $char1024. HAS_LOCATION : $char1024. HAS_NOTDONE : $char1024.;run;proc sort data=odm out=ordered; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey FormOID FormRepeatKey; run;data visits; length VSDTC POSITION TEMPLOC BPNOTDONE BPREASND $1024; retain VSDTC POSITION TEMPLOC BPNOTDONE BPREASND; set ordered; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey FormOID FormRepeatKey; if first.FormRepeatKey then do; VSDTC=''; POSITION=''; TEMPLOC=''; BPNOTDONE=''; BPREASND=''; end; if ItemOID='IT.VS.VSDTC' then VSDTC=Value; if ItemOID='IT.VS.POSITION' then POSITION=Value; if ItemOID='IT.VS.TEMPLOC' then TEMPLOC=Value; if ItemOID='IT.VS.BPNOTDONE' then BPNOTDONE=Value; if ItemOID='IT.VS.BPREASND' then BPREASND=Value; if last.FormRepeatKey then output;run;data tests; set vs_tests; length RESULTOID UNITOID $1024; RESULTOID=cats('IT.VS.',VSTESTCD); UNITOID=cats(RESULTOID,'U'); ORD=input(TESTORD,best32.); run;proc sql; create table grid as select a.*,b.* from visits a cross join tests b;create table joined as select a.*,b.Value as RESULTTEXT,c.Value as UNITTEXT from grid a left join odm b on a.StudyOID=b.StudyOID and a.SubjectKey=b.SubjectKey and a.StudyEventOID=b.StudyEventOID and a.StudyEventRepeatKey=b.StudyEventRepeatKey and a.FormOID=b.FormOID and a.FormRepeatKey=b.FormRepeatKey and a.RESULTOID=b.ItemOID left join odm c on a.StudyOID=c.StudyOID and a.SubjectKey=c.SubjectKey and a.StudyEventOID=c.StudyEventOID and a.StudyEventRepeatKey=c.StudyEventRepeatKey and a.FormOID=c.FormOID and a.FormRepeatKey=c.FormRepeatKey and a.UNITOID=c.ItemOID; quit;data prepared; set joined; length DOMAIN $2 STUDYID USUBJID VISIT VSORRES VSORRESU VSSTRESC VSSTRESU VSPOS VSSTAT VSREASND VSLOC $1024;DOMAIN='VS'; STUDYID=StudyOID; USUBJID=SubjectKey; VISIT=StudyEventOID; VSORRES=''; VSORRESU=''; VSSTRESN=.; VSSTRESC=''; VSSTRESU=''; VSPOS=''; VSSTAT=''; VSREASND=''; VSLOC='';if HAS_NOTDONE='1' and BPNOTDONE='Y' then do; VSSTAT='NOT DONE'; VSREASND=BPREASND; end;else if not missing(RESULTTEXT) then do; VSORRES=RESULTTEXT; VSORRESU=UNITTEXT; VSSTRESN=input(RESULTTEXT,best32.); VSSTRESC=strip(put(VSSTRESN,best15.)); VSSTRESU=UNITTEXT; if HAS_POSITION='1' then VSPOS=POSITION; if HAS_LOCATION='1' then VSLOC=TEMPLOC; end; run;proc sort data=prepared; by STUDYID USUBJID VSDTC ORD; run;data result; set prepared; by STUDYID USUBJID; retain VSSEQ; if first.USUBJID then VSSEQ=0; VSSEQ+1;run;proc sql; create table final as select DOMAIN, STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSSTAT, VSREASND, VSLOC, VSDTC from result;quit;proc export data=final outfile="/app/output/vs.csv" dbms=csv replace; run;
yamaa