Review ask

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

Lifecycle: draftEditUnder review
What this benchmark means
README.md

Public entries: Python and R. Passing coverage requires an installed inventory.

Goal: build the administered exposure records from the exposure as collected: tablet counts, a weight-based dose, a blinded kit, and an AUC target, adding EXSEQ, EXTRT, EXDOSE, EXDOSU, EXSTDTC, and EXENDTC.

Input: long-form Operational Data Model (ODM) data with one row per collected item, carrying the study, subject, visit, form, and form repeat (StudyOID, SubjectKey, StudyEventOID, FormOID, FormRepeatKey), the item (ItemOID), and the stored value (Value). Exposure items live on the FO.EC form (IT.EC.ECOCCUR, IT.EC.ECTRT, IT.EC.FORM, IT.EC.TABLETS, IT.EC.STRENGTHMG, IT.EC.DOSEMKG, IT.EC.AUCTARGET, IT.EC.KIT, IT.EC.ECSTDTC, IT.EC.ECENDTC); body weight is the IT.VS.WEIGHT item on the FO.VS form at the same visit. The kit list comes from the interactive response technology (IRT) system, not electronic data capture (EDC), so it stays a separate input (input/kit_list.csv) mapping kit numbers to treatments and doses.

Variables:

  • EXSEQ is the order of the administration within the subject, numbered by start date and then by the form's repeat number within the visit.
  • EXTRT is the administered treatment: the kit list's treatment for a blinded kit, otherwise as collected.
  • EXDOSE is the administered dose: tablets times strength, the mg/kg dose times the visit's body weight, the kit list's dose for a blinded kit, and the AUC target as collected.
  • EXDOSU is mg, except the AUC target keeps AUC.

Note: only a form whose occurrence item (IT.EC.ECOCCUR) is Y gives an exposure record, so a skipped dose leaves none. The placebo kit keeps its zero dose.

Inputs

Under review
2 source files

input/kit_list.csv

Edit
2 rows
input/kit_list.csv
KITTREATMENTDOSE_MG
KIT-104DRUG X100
KIT-207PLACEBO0

input/odm.csv

Edit
55 rows
input/odm.csv
StudyOIDMetaDataVersionOIDSubjectKeyStudyEventOIDStudyEventRepeatKeyFormOIDFormRepeatKeyItemGroupOIDItemGroupRepeatKeyItemOIDValue
STUDY01MDV.1001V11FO.EC1IG.EC1IT.EC.ECOCCURY
STUDY01MDV.1001V11FO.EC1IG.EC1IT.EC.ECTRTFINASTERIDE
STUDY01MDV.1001V11FO.EC1IG.EC1IT.EC.FORMTABLET
STUDY01MDV.1001V11FO.EC1IG.EC1IT.EC.TABLETS2
STUDY01MDV.1001V11FO.EC1IG.EC1IT.EC.STRENGTHMG5
STUDY01MDV.1001V11FO.EC1IG.EC1IT.EC.ECSTDTC2025-01-02
STUDY01MDV.1001V11FO.EC1IG.EC1IT.EC.ECENDTC2025-01-02
STUDY01MDV.1001V11FO.EC2IG.EC1IT.EC.ECOCCURN
STUDY01MDV.1001V11FO.EC2IG.EC1IT.EC.ECTRTFINASTERIDE
STUDY01MDV.1001V11FO.EC2IG.EC1IT.EC.FORMTABLET
STUDY01MDV.1001V11FO.EC2IG.EC1IT.EC.TABLETS2
STUDY01MDV.1001V11FO.EC2IG.EC1IT.EC.STRENGTHMG5
STUDY01MDV.1001V11FO.EC2IG.EC1IT.EC.ECSTDTC2025-01-09
STUDY01MDV.1001V11FO.EC2IG.EC1IT.EC.ECENDTC2025-01-09
STUDY01MDV.1001V11FO.VS1IG.VS1IT.VS.WEIGHT70
STUDY01MDV.1001V11FO.VS1IG.VS1IT.VS.HEIGHT170
STUDY01MDV.1001V11FO.VS1IG.VS1IT.VS.VSDTC2025-01-02
STUDY01MDV.1001V21FO.EC1IG.EC1IT.EC.ECOCCURY
STUDY01MDV.1001V21FO.EC1IG.EC1IT.EC.ECTRTTRASTUZUMAB
STUDY01MDV.1001V21FO.EC1IG.EC1IT.EC.FORMINFUSION
STUDY01MDV.1001V21FO.EC1IG.EC1IT.EC.DOSEMKG6
STUDY01MDV.1001V21FO.EC1IG.EC1IT.EC.ECSTDTC2025-01-23
STUDY01MDV.1001V21FO.EC1IG.EC1IT.EC.ECENDTC2025-01-23
STUDY01MDV.1001V21FO.VS1IG.VS1IT.VS.WEIGHT72
STUDY01MDV.1001V21FO.VS1IG.VS1IT.VS.VSDTC2025-01-23
STUDY01MDV.1002V11FO.EC1IG.EC1IT.EC.ECOCCURY
STUDY01MDV.1002V11FO.EC1IG.EC1IT.EC.FORMKITDOSE
STUDY01MDV.1002V11FO.EC1IG.EC1IT.EC.KITKIT-104
STUDY01MDV.1002V11FO.EC1IG.EC1IT.EC.ECSTDTC2025-02-05
STUDY01MDV.1002V11FO.EC1IG.EC1IT.EC.ECENDTC2025-02-05
STUDY01MDV.1002V11FO.VS1IG.VS1IT.VS.WEIGHT80
STUDY01MDV.1002V11FO.VS1IG.VS1IT.VS.VSDTC2025-02-05
STUDY01MDV.1002V21FO.EC1IG.EC1IT.EC.ECTRTTRASTUZUMAB
STUDY01MDV.1002V21FO.EC1IG.EC1IT.EC.FORMINFUSION
STUDY01MDV.1002V21FO.EC1IG.EC1IT.EC.DOSEMKG6
STUDY01MDV.1002V21FO.EC1IG.EC1IT.EC.ECOCCURY
STUDY01MDV.1002V21FO.EC1IG.EC1IT.EC.ECSTDTC2025-02-26
STUDY01MDV.1002V21FO.EC1IG.EC1IT.EC.ECENDTC2025-02-26
STUDY01MDV.1002V21FO.VS1IG.VS1IT.VS.WEIGHT80
STUDY01MDV.1002V21FO.VS1IG.VS1IT.VS.VSDTC2025-02-26
STUDY01MDV.1003V11FO.EC1IG.EC1IT.EC.ECOCCURY
STUDY01MDV.1003V11FO.EC1IG.EC1IT.EC.FORMKITDOSE
STUDY01MDV.1003V11FO.EC1IG.EC1IT.EC.KITKIT-207
STUDY01MDV.1003V11FO.EC1IG.EC1IT.EC.ECSTDTC2025-03-04
STUDY01MDV.1003V11FO.EC1IG.EC1IT.EC.ECENDTC2025-03-04
STUDY01MDV.1003V11FO.VS1IG.VS1IT.VS.WEIGHT65
STUDY01MDV.1003V11FO.VS1IG.VS1IT.VS.VSDTC2025-03-04
STUDY01MDV.1003V21FO.EC1IG.EC1IT.EC.ECOCCURY
STUDY01MDV.1003V21FO.EC1IG.EC1IT.EC.ECTRTCARBOPLATIN
STUDY01MDV.1003V21FO.EC1IG.EC1IT.EC.FORMAUCDOSE
STUDY01MDV.1003V21FO.EC1IG.EC1IT.EC.AUCTARGET5
STUDY01MDV.1003V21FO.EC1IG.EC1IT.EC.ECSTDTC2025-03-25
STUDY01MDV.1003V21FO.EC1IG.EC1IT.EC.ECENDTC2025-03-25
STUDY01MDV.1003V21FO.VS1IG.VS1IT.VS.WEIGHT65
STUDY01MDV.1003V21FO.VS1IG.VS1IT.VS.VSDTC2025-03-25

Expected output

Under review
6 expected rows

expected/ex.csv

Edit
6 rows
expected/ex.csv
DOMAINSTUDYIDUSUBJIDEXSEQEXTRTEXDOSEEXDOSUEXSTDTCEXENDTC
EXSTUDY011FINASTERIDE10mg2025-01-022025-01-02
EXSTUDY012TRASTUZUMAB432mg2025-01-232025-01-23
EXSTUDY011DRUG X100mg2025-02-052025-02-05
EXSTUDY012TRASTUZUMAB480mg2025-02-262025-02-26
EXSTUDY011PLACEBO0mg2025-03-042025-03-04
EXSTUDY012CARBOPLATIN5AUC2025-03-252025-03-25
Shaded columns are derived— = missing value

Human review and assessment

Your review

3 questions and your comments · View benchmark discussions

Mapping spec

Generated from the benchmark YAML; do not edit by hand

Variable NameVariable LabelTypeLengthControlled Terms or FormatOriginCoreConversion DefinitionVariable TypeVariable OrderComments for Define
DOMAINDomain AbbreviationCharAssignedSet constant value "EX".SDTM1
STUDYIDStudy IdentifierCharCollectedCopy value from ODM.StudyOID.SDTM2
USUBJIDUnique Subject IdentifierCharCollectedCopy value from ODM.SubjectKey.SDTM3
EXTRTName of TreatmentCharDerivedIf FORM = 'KITDOSE' then "KITMAP.TREATMENT"; otherwise "EC_ECTRT.Value".SDTM4
EXDOSEDoseNumDerivedIf FORM = 'TABLET' then compute TABLETS * STRENGTHMG; If FORM = 'INFUSION' then compute DOSEMKG * WT_KG; If FORM = 'AUCDOSE' then "AUCTARGET"; otherwise "KIT_DOSE".SDTM5
EXDOSUDose UnitsCharmg, AUCDerivedIf FORM = 'AUCDOSE' then "AUC"; otherwise "mg".SDTM6
EXSTDTCStart Date/Time of TreatmentCharISO 8601DerivedTake Value from the ODM record whose StudyOID equals ODM.StudyOID, SubjectKey equals ODM.SubjectKey, StudyEventOID equals ODM.StudyEventOID, FormOID equals ODM.FormOID and FormRepeatKey equals ODM.FormRepeatKey, where ODM.ItemOID = 'IT.EC.ECSTDTC', and a row with no match is an error.SDTM7
EXENDTCEnd Date/Time of TreatmentCharISO 8601DerivedTake Value from the ODM record whose StudyOID equals ODM.StudyOID, SubjectKey equals ODM.SubjectKey, StudyEventOID equals ODM.StudyEventOID, FormOID equals ODM.FormOID and FormRepeatKey equals ODM.FormRepeatKey, where ODM.ItemOID = 'IT.EC.ECENDTC', and a row with no match is an error.SDTM8
EXSEQSequence NumberNumDerivedRow number within each group of STUDYID, USUBJID, ordered by EXSTDTC then FRMREP.SDTM9
TemplateInclude when
administeredODM.ItemOID = 'IT.EC.ECOCCUR' AND ODM.Value = 'Y'
VariablePermitted values
EXDOSUmg, AUC
VersionDateAuthorDescriptionReviewerSign-off
1.72026-10-20yamaa docs buildRead 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.62026-10-17yamaa docs buildDescribe collected date parts written as ISO 8601 date text and times of day counted in seconds since midnight
1.52026-10-17yamaa docs buildName 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.42026-10-15yamaa docs buildState datetime precision codes through the hour and minute, and complete only the time parts a collected datetime lacks
1.32026-09-29yamaa docs buildShow study codelist names, external dictionaries, and ISO 8601 formats in variable rows; list the document's codelist values and metadata
1.22026-09-29yamaa docs buildClassify ADaM copies from a declared input as Predecessor and recodes as Derived, and SDTM recodes of locally derived columns as Derived
1.12026-09-29yamaa docs buildResolve lookups into the donor record they select, and give every derivation shape a plain-language rule instead of its raw YAML
1.0yamaa docs buildInitial generation from spec.yaml

Agent prompt

The request an agent receives with the input files; each tier states no more than the one above

prompts/sdtm-ex-from-ec/full.md

Edit
26 lines

Following CDISC SDTM standards, use the provided KIT_LIST and ODM datasets to create an EX dataset with one record per administration.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, EXSEQ, EXTRT, EXDOSE, EXDOSU, EXSTDTC, EXENDTC

DOMAIN is "EX". STUDYID is the ODM study OID and USUBJID is the ODM subject key, each as given. Only a form whose occurrence item (IT.EC.ECOCCUR) is "Y" gives a record; a skipped dose gives none. EXTRT is the treatment as collected, except that a record whose collected form is "KITDOSE" has the treatment the kit list holds for its kit number. A record whose collected form is "TABLET" has EXDOSE as the number of tablets times the tablet strength. A record whose collected form is "INFUSION" has EXDOSE as the collected mg/kg dose times the body weight collected at the same visit. A record whose collected form is "KITDOSE" has EXDOSE as the dose the kit list holds for its kit number, and a kit whose listed dose is zero keeps that zero. A record whose collected form is "AUCDOSE" has EXDOSE as the AUC target as collected. EXDOSE is not rounded. EXDOSU is "AUC" for a record whose collected form is "AUCDOSE" and "mg" for every other record. EXSTDTC and EXENDTC are the start and end dates collected on the form. EXSEQ numbers each subject's records from 1 by start date.

Read the source datasets from /app/input and save the completed dataset as /app/output/ex.csv.

prompts/sdtm-ex-from-ec/conventions.md

Edit
10 lines

Following CDISC SDTM standards, use the provided KIT_LIST and ODM datasets to create an EX dataset with one record per administration.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, EXSEQ, EXTRT, EXDOSE, EXDOSU, EXSTDTC, EXENDTC

DOMAIN is "EX". EXDOSE is not rounded.

Read the source datasets from /app/input and save the completed dataset as /app/output/ex.csv.

prompts/sdtm-ex-from-ec/brief.md

Edit
8 lines

Following CDISC SDTM standards, use the provided KIT_LIST and ODM datasets to create an EX dataset with one record per administration.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, EXSEQ, EXTRT, EXDOSE, EXDOSU, EXSTDTC, EXENDTC

Read the source datasets from /app/input and save the completed dataset as /app/output/ex.csv.

yamaa code

Public calls generated from benchmark.yaml; results must match the expected output

R

Edit
3 lines
library(yamaa)yamaa_spec("spec.yaml")$run()$output

Python

Edit
3 lines
import yamaayamaa.spec("spec.yaml").run().output

Solution without yamaa

Written from the full prompt and the inputs alone; CI checks it reproduces the expected output

solutions/sdtm-ex-from-ec/result.R

Edit
73 lines
# Reference solution for the yamaa benchmark sdtm-ex-from-ec (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(readr)library(tidyr)odm <- read_csv(  "/app/input/odm.csv",  col_types = cols(.default = col_character()))kit <- read_csv(  "/app/input/kit_list.csv",  col_types = cols(.default = col_character()))kit_trt <- setNames(kit$TREATMENT, kit$KIT)kit_dose <- setNames(suppressWarnings(as.numeric(kit$DOSE_MG)), kit$KIT)ec_forms <- odm |> filter(FormOID == "FO.EC")vs_weight <- odm |>  filter(FormOID == "FO.VS", ItemOID == "IT.VS.WEIGHT") |>  group_by(StudyOID, SubjectKey, StudyEventOID, StudyEventRepeatKey) |>  summarise(WEIGHT = suppressWarnings(as.numeric(first(Value))), .groups = "drop")ec_wide <- ec_forms |>  group_by(StudyOID, SubjectKey, StudyEventOID, StudyEventRepeatKey, FormRepeatKey) |>  summarise(    ECOCCUR = Value[ItemOID == "IT.EC.ECOCCUR"][1],    ECTRT = Value[ItemOID == "IT.EC.ECTRT"][1],    FORM = Value[ItemOID == "IT.EC.FORM"][1],    TABLETS = suppressWarnings(as.numeric(Value[ItemOID == "IT.EC.TABLETS"][1])),    STRENGTHMG = suppressWarnings(as.numeric(Value[ItemOID == "IT.EC.STRENGTHMG"][1])),    DOSEMKG = suppressWarnings(as.numeric(Value[ItemOID == "IT.EC.DOSEMKG"][1])),    KIT = Value[ItemOID == "IT.EC.KIT"][1],    AUCTARGET = suppressWarnings(as.numeric(Value[ItemOID == "IT.EC.AUCTARGET"][1])),    ECSTDTC = Value[ItemOID == "IT.EC.ECSTDTC"][1],    ECENDTC = Value[ItemOID == "IT.EC.ECENDTC"][1],    .groups = "drop"  ) |>  left_join(vs_weight, by = c("StudyOID", "SubjectKey", "StudyEventOID", "StudyEventRepeatKey")) |>  filter(ECOCCUR == "Y")ex <- ec_wide |>  rowwise() |>  mutate(    DOMAIN = "EX",    STUDYID = StudyOID,    USUBJID = SubjectKey,    EXTRT = if (FORM == "KITDOSE") unname(kit_trt[KIT]) else ECTRT,    EXDOSE = case_when(      FORM == "TABLET" ~ TABLETS * STRENGTHMG,      FORM == "INFUSION" ~ DOSEMKG * WEIGHT,      FORM == "KITDOSE" ~ unname(kit_dose[KIT]),      FORM == "AUCDOSE" ~ AUCTARGET,      .default = NA_real_    ),    EXDOSU = ifelse(FORM == "AUCDOSE", "AUC", "mg"),    FORMREPEAT = suppressWarnings(as.integer(FormRepeatKey)),    EXSTDTC_DATE = suppressWarnings(as.Date(na_if(ECSTDTC, "")))  ) |>  ungroup() |>  arrange(STUDYID, USUBJID, EXSTDTC_DATE, FORMREPEAT) |>  group_by(STUDYID, USUBJID) |>  mutate(EXSEQ = row_number()) |>  ungroup() |>  select(DOMAIN, STUDYID, USUBJID, EXSEQ, EXTRT, EXDOSE, EXDOSU, EXSTDTC = ECSTDTC, EXENDTC = ECENDTC)dir.create("/app/output", showWarnings = FALSE)write_csv(ex, "/app/output/ex.csv", na = "")

solutions/sdtm-ex-from-ec/result.py

Edit
144 lines
# Reference solution for the yamaa benchmark sdtm-ex-from-ec (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.from pathlib import Pathimport polars as plodm = pl.read_csv("/app/input/odm.csv", infer_schema=False)kit = pl.read_csv("/app/input/kit_list.csv", infer_schema=False)kit_map = {r["KIT"]: (r["TREATMENT"], r["DOSE_MG"]) for r in kit.to_dicts()}ec_forms = odm.filter(pl.col("FormOID") == "FO.EC")vs_forms = odm.filter(pl.col("FormOID") == "FO.VS")# Body weight per visit: SubjectKey + StudyEventOID + StudyEventRepeatKey.vs_wide = (    vs_forms.filter(pl.col("ItemOID") == "IT.VS.WEIGHT")    .group_by("StudyOID", "SubjectKey", "StudyEventOID", "StudyEventRepeatKey")    .agg(pl.col("Value").first().alias("WEIGHT"))    .with_columns(pl.col("WEIGHT").cast(pl.Float64, strict=False)))ec_wide = (    ec_forms.group_by(        "StudyOID",        "SubjectKey",        "StudyEventOID",        "StudyEventRepeatKey",        "FormRepeatKey",    )    .agg(        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.ECOCCUR").first().alias("ECOCCUR"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.ECTRT").first().alias("ECTRT"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.FORM").first().alias("FORM"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.TABLETS").first().alias("TABLETS"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.STRENGTHMG").first().alias("STRENGTHMG"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.DOSEMKG").first().alias("DOSEMKG"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.KIT").first().alias("KIT"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.AUCTARGET").first().alias("AUCTARGET"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.ECSTDTC").first().alias("ECSTDTC"),        pl.col("Value").filter(pl.col("ItemOID") == "IT.EC.ECENDTC").first().alias("ECENDTC"),    )    .join(        vs_wide,        on=["StudyOID", "SubjectKey", "StudyEventOID", "StudyEventRepeatKey"],        how="left",    )    .filter(pl.col("ECOCCUR") == "Y"))def num(v):    if v is None or v == "":        return None    try:        return float(v)    except ValueError:        return Nonerecords: list[dict] = []for row in ec_wide.to_dicts():    study = row["StudyOID"]    subj = row["SubjectKey"]    form = row["FORM"] or ""    usubjid = subj    if form == "KITDOSE":        treatment, dose_mg = kit_map.get(row["KIT"] or "", (None, None))        trt = treatment        dose = num(dose_mg)        dosu = "mg"    elif form == "TABLET":        trt = row["ECTRT"] or None        tablets = num(row["TABLETS"])        strength = num(row["STRENGTHMG"])        dose = tablets * strength if tablets is not None and strength is not None else None        dosu = "mg"    elif form == "INFUSION":        trt = row["ECTRT"] or None        perkg = num(row["DOSEMKG"])        weight = row["WEIGHT"]        dose = perkg * weight if perkg is not None and weight is not None else None        dosu = "mg"    elif form == "AUCDOSE":        trt = row["ECTRT"] or None        dose = num(row["AUCTARGET"])        dosu = "AUC"    else:        trt = row["ECTRT"] or None        dose = None        dosu = "mg"    records.append(        {            "STUDYID": study,            "USUBJID": usubjid,            "EXTRT": trt,            "EXDOSE": dose,            "EXDOSU": dosu,            "EXSTDTC": row["ECSTDTC"] or None,            "EXENDTC": row["ECENDTC"] or None,            "FORMREPEAT": int(row["FormRepeatKey"]) if row["FormRepeatKey"] else 0,        }    )ex = (    pl.DataFrame(        records,        schema={            "STUDYID": pl.String,            "USUBJID": pl.String,            "EXTRT": pl.String,            "EXDOSE": pl.Float64,            "EXDOSU": pl.String,            "EXSTDTC": pl.String,            "EXENDTC": pl.String,            "FORMREPEAT": pl.Int64,        },    )    .with_columns(        DOMAIN=pl.lit("EX"),        EXSTDTC_DT=pl.col("EXSTDTC").str.to_date(strict=False),    )    .sort(["STUDYID", "USUBJID", "EXSTDTC_DT", "FORMREPEAT"], nulls_last=True)    .with_columns(EXSEQ=pl.col("USUBJID").cum_count().over(["STUDYID", "USUBJID"]))    .with_columns(EXSEQ=pl.col("EXSEQ").cast(pl.Int64))    .select(        "DOMAIN",        "STUDYID",        "USUBJID",        "EXSEQ",        "EXTRT",        "EXDOSE",        "EXDOSU",        "EXSTDTC",        "EXENDTC",    ))Path("/app/output").mkdir(exist_ok=True)ex.write_csv("/app/output/ex.csv")

solutions/sdtm-ex-from-ec/result.sas

Edit
70 lines
/* opensas reference solution for the yamaa benchmark sdtm-ex-from-ec.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data kit_list;  length KIT TREATMENT DOSE_MG $1024;  infile "/app/input/kit_list.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input KIT : $char1024. TREATMENT : $char1024. DOSE_MG : $char1024.;run;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;proc sort data=odm out=ordered; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey FormRepeatKey; run;data doses;  length ECTRT ECSTDTC ECENDTC ECOCCUR FORM TABLETS STRENGTHMG DOSEMKG KIT AUCTARGET $1024;  retain ECTRT ECSTDTC ECENDTC ECOCCUR FORM TABLETS STRENGTHMG DOSEMKG KIT AUCTARGET;  set ordered;  where FormOID='FO.EC';  by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey FormRepeatKey;  if first.FormRepeatKey then do;    ECTRT=''; ECSTDTC=''; ECENDTC=''; ECOCCUR=''; FORM=''; TABLETS=''; STRENGTHMG=''; DOSEMKG=''; KIT=''; AUCTARGET='';  end;    if ItemOID='IT.EC.ECTRT' then ECTRT=Value;  if ItemOID='IT.EC.ECSTDTC' then ECSTDTC=Value;  if ItemOID='IT.EC.ECENDTC' then ECENDTC=Value;  if ItemOID='IT.EC.ECOCCUR' then ECOCCUR=Value;  if ItemOID='IT.EC.FORM' then FORM=Value;  if ItemOID='IT.EC.TABLETS' then TABLETS=Value;  if ItemOID='IT.EC.STRENGTHMG' then STRENGTHMG=Value;  if ItemOID='IT.EC.DOSEMKG' then DOSEMKG=Value;  if ItemOID='IT.EC.KIT' then KIT=Value;  if ItemOID='IT.EC.AUCTARGET' then AUCTARGET=Value;  if last.FormRepeatKey then output;run;proc sort data=odm out=ordered; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey; run;data weights;  length VISITWEIGHT $1024;  retain VISITWEIGHT;  set ordered;  where FormOID='FO.VS';  by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey;  if first.StudyEventRepeatKey then do;    VISITWEIGHT='';  end;    if ItemOID='IT.VS.WEIGHT' then VISITWEIGHT=Value;  if last.StudyEventRepeatKey then output;run;proc sql;create table joined as select a.*,b.VISITWEIGHT,c.TREATMENT,c.DOSE_MG from doses a left join weights b on a.StudyOID=b.StudyOID and a.SubjectKey=b.SubjectKey and a.StudyEventOID=b.StudyEventOID and a.StudyEventRepeatKey=b.StudyEventRepeatKey left join kit_list c on a.KIT=c.KIT; quit;data prepared; set joined; if ECOCCUR='Y'; length DOMAIN $2 STUDYID USUBJID EXTRT EXDOSU EXSTDTC EXENDTC $1024;DOMAIN='EX'; STUDYID=StudyOID; USUBJID=SubjectKey; EXTRT=ECTRT; EXSTDTC=ECSTDTC; EXENDTC=ECENDTC; EXDOSU='mg'; EXDOSE=.;if FORM='TABLET' then EXDOSE=input(TABLETS,best32.)*input(STRENGTHMG,best32.);if FORM='INFUSION' then EXDOSE=input(DOSEMKG,best32.)*input(VISITWEIGHT,best32.);if FORM='KITDOSE' then do; EXTRT=TREATMENT; EXDOSE=input(DOSE_MG,best32.); end;if FORM='AUCDOSE' then do; EXDOSE=input(AUCTARGET,best32.); EXDOSU='AUC'; end;FORMREP=input(FormRepeatKey,best32.); NODATE=missing(EXSTDTC); run;proc sort data=prepared; by STUDYID USUBJID NODATE EXSTDTC FORMREP; run;data result; set prepared; by STUDYID USUBJID; retain EXSEQ;  if first.USUBJID then EXSEQ=0;  EXSEQ+1;run;proc sql;  create table final as select DOMAIN, STUDYID, USUBJID, EXSEQ, EXTRT, EXDOSE, EXDOSU, EXSTDTC, EXENDTC from result;quit;proc export data=final outfile="/app/output/ex.csv" dbms=csv replace; run;