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 progression-free survival record for each subject under the fixed PFS code (Progression-Free Survival), setting STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, and SRCSEQ.

Input: randomization dates (RANDDT); tumor response assessments for overall response (RSTESTCD of OVRLRESP) with result (RSSTRESC), assessment date (RSDTC), and adequacy flag (ADEQFL of Y); disposition records with outcome (DSDECOD of DEATH) and date (DSDTC); and the start date of new anti-cancer therapy (NTXSTDT) for subjects who began one. Records sharing a date are ordered by their sequence number.

Variables:

  • STARTDT is the randomization date from RANDDT.
  • ADT is the event date, the earlier of the progression and death dates, when the subject has an event; else the last adequate assessment date, or the randomization date when no adequate assessment can supply a censoring date.
  • AVAL is the number of days from STARTDT through ADT, counting the randomization day as day one.
  • CNSR is 0 when an event occurred (a progression or death date is present), 1 otherwise. The event always wins: a death after the last adequate assessment is still an event, unless new anti-cancer therapy started first.
  • EVNTDESC is DISEASE PROGRESSION for a progression event, DEATH for a death event, and CENSORED when both dates are absent. A progression and a death on the same day count as progression.
  • SRCDOM, SRCVAR, and SRCSEQ trace ADT to its source: DS with DSDTC and the disposition sequence number for a death event, or RS with RSDTC and the response sequence number for a progression event or a censored assessment. All three are empty when the subject is censored at randomization.

Note: only assessments flagged adequate can supply a progression event or a censoring date, so an inadequate progression assessment leaves the subject censored at the last adequate dated assessment. An assessment without a date cannot be the last dated assessment. When new anti-cancer therapy started (NTXSTDT present), only dates on or before therapy start are usable: a progression or death dated after therapy start is not an event, and the subject is censored at the last adequate assessment dated on or before therapy start. A subject with no usable adequate assessment at all is censored at randomization, for a 1-day PFS.

Inputs

Under review
3 source files

input/adsl.csv

Edit
12 rows
input/adsl.csv
STUDYIDUSUBJIDRANDDTNTXSTDT
YAMAA-012026-01-01—
YAMAA-012026-01-01—
YAMAA-012026-02-01—
YAMAA-012026-01-15—
YAMAA-012026-03-01—
YAMAA-012026-01-01—
YAMAA-012026-01-01—
YAMAA-012026-02-01—
YAMAA-012026-04-01—
YAMAA-012026-01-012026-03-01
YAMAA-012026-05-01—
YAMAA-012026-01-012026-02-01

input/ds.csv

Edit
6 rows
input/ds.csv
STUDYIDUSUBJIDDSSEQDSDTCDSDECOD
YAMAA-0112026-03-21DEATH
YAMAA-0112026-01-30DEATH
YAMAA-0112026-03-15DEATH
YAMAA-0112026-05-01DEATH
YAMAA-0122026-05-01DEATH
YAMAA-0112026-03-15DEATH

input/rs.csv

Edit
25 rows
input/rs.csv
STUDYIDUSUBJIDRSSEQRSTESTCDRSDTCRSSTRESCADEQFL
YAMAA-011OVRLRESP2026-01-01SDY
YAMAA-012OVRLRESP2026-02-19PDY
YAMAA-011OVRLRESP2026-01-01SDY
YAMAA-012OVRLRESP2026-01-22SDY
YAMAA-011OVRLRESP2026-02-01SDY
YAMAA-012OVRLRESP2026-03-14SDY
YAMAA-011OVRLRESP2026-01-15SDY
YAMAA-012OVRLRESP2026-02-05SDY
YAMAA-013OVRLRESP2026-03-06PDN
YAMAA-011OVRLRESP2026-03-01SDY
YAMAA-012OVRLRESP—SDY
YAMAA-011OVRLRESP2026-01-01SDY
YAMAA-012OVRLRESP2026-03-15PDY
YAMAA-011OVRLRESP2026-01-01SDY
YAMAA-012OVRLRESP2026-04-01PDY
YAMAA-013OVRLRESP2026-04-01PDY
YAMAA-011OVRLRESP2026-02-01SDY
YAMAA-012OVRLRESP2026-03-01SDY
YAMAA-011OVRLRESP2026-04-15SDY
YAMAA-011OVRLRESP2026-01-15SDY
YAMAA-012OVRLRESP2026-03-01SDY
YAMAA-013OVRLRESP2026-04-01PDY
YAMAA-011OVRLRESP—SDY
YAMAA-012OVRLRESP2026-06-01PDN
YAMAA-011OVRLRESP2026-01-10SDY

Expected output

Under review
12 expected rows

expected/adtte.csv

Edit
12 rows
expected/adtte.csv
STUDYIDUSUBJIDPARAMCDPARAMSTARTDTADTAVALCNSREVNTDESCSRCDOMSRCVARSRCSEQ
YAMAA-01PFSProgression-Free Survival2026-01-012026-02-19500DISEASE PROGRESSIONRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-01-012026-01-30300DEATHDSDSDTC1
YAMAA-01PFSProgression-Free Survival2026-02-012026-03-14421CENSOREDRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-01-152026-02-05221CENSOREDRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-03-012026-03-0111CENSOREDRSRSDTC1
YAMAA-01PFSProgression-Free Survival2026-01-012026-03-15740DISEASE PROGRESSIONRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-01-012026-04-01910DISEASE PROGRESSIONRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-02-012026-05-01900DEATHDSDSDTC1
YAMAA-01PFSProgression-Free Survival2026-04-012026-04-15151CENSOREDRSRSDTC1
YAMAA-01PFSProgression-Free Survival2026-01-012026-03-01601CENSOREDRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-05-012026-05-0111CENSORED———
YAMAA-01PFSProgression-Free Survival2026-01-012026-01-10101CENSOREDRSRSDTC1
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

Dataset NameVariable NameVariable LabelTypeCodelist/Controlled TermsCoreComputational MethodOrigin
ADTTESTUDYIDStudy IdentifiercharacterCopy value from ADSL.STUDYID.Predecessor
ADTTEUSUBJIDUnique Subject IdentifiercharacterCopy value from ADSL.USUBJID.Predecessor
ADTTEPARAMCDParameter CodecharacterSet constant value "PFS".Assigned
ADTTEPARAMParametercharacterSet constant value "Progression-Free Survival".Assigned
ADTTESTARTDTTime to Event Origin Date for SubjectnumericISO 8601Copy value from ADSL.RANDDT.Predecessor
ADTTECNSRCensornumericIf EVENTDT IS NOT NULL then "0"; otherwise "1".Derived
ADTTEADTAnalysis DatenumericISO 8601First non-missing of EVENTDT and CENSORDT.Derived
ADTTEAVALAnalysis ValuenumericWhole days from STARTDT to ADT, counting both endpoints.Derived
ADTTEEVNTDESCEvent or Censoring DescriptioncharacterIf EVENTDT IS NULL then "CENSORED"; If PDDT IS NOT NULL AND (DEATHDT IS NULL OR PDDT <= DEATHDT) then "DISEASE PROGRESSION"; otherwise "DEATH".Derived
ADTTESRCDOMSource DatacharacterIf EVNTDESC = 'DEATH' then "DS"; If CENSORASSESSDT IS NULL then "None"; otherwise "RS".Derived
ADTTESRCVARSource VariablecharacterIf EVNTDESC = 'DEATH' then "DSDTC"; If CENSORASSESSDT IS NULL then "None"; otherwise "RSDTC".Derived
ADTTESRCSEQSource Sequence NumbernumericIf EVNTDESC = 'DISEASE PROGRESSION' then "PROGRESSION.RSSEQ"; If EVNTDESC = 'DEATH' then "DEATH.DSSEQ"; If CENSORASSESSDT IS NULL then "None"; otherwise "CENSORSEQ".Derived
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/adam-adtte-pfs/full.md

Edit
39 lines

Following CDISC ADaM standards, use the provided ADSL, DS, and RS datasets to create an ADTTE dataset with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, SRCSEQ

PARAMCD is "PFS" and PARAM is "Progression-Free Survival" for every record. STARTDT is the subject's randomization date, RANDDT in ADSL. The progression date is the earliest date of an overall response assessment (RSTESTCD "OVRLRESP") with a result of "PD" (RSSTRESC) that is flagged adequate (ADEQFL "Y") and has a date; when assessments share that date, the one with the lowest sequence number is the progression record. The death date is the earliest date of a disposition record with an outcome of "DEATH" (DSDECOD), the record with the lowest sequence number supplying it when records share that date. Only assessments flagged adequate can supply a progression event or a censoring date, and an assessment without a date can supply neither, so an inadequate progression assessment leaves the subject censored at the last adequate dated assessment. When the subject started new anti-cancer therapy (NTXSTDT in ADSL), only dates on or before the therapy start date are usable: a progression or death dated after the therapy start date is not an event, and censoring uses only adequate assessments dated on or before the therapy start date. ADT is the earlier of the progression and death dates when either is present, and CNSR is 0 then; a death after the last adequate assessment is still an event. When neither date is present, CNSR is 1 and ADT is the date of the subject's last adequate dated overall response assessment. A subject with no usable adequate assessment at all is censored at the randomization date: ADT is STARTDT, AVAL is 1, and SRCDOM, SRCVAR, and SRCSEQ have no value. AVAL is the number of days from STARTDT through ADT, counting the randomization day as day one. EVNTDESC is "DISEASE PROGRESSION" for a progression event, "DEATH" for a death event, and "CENSORED" when both dates are absent; a progression and a death on the same day count as progression. SRCDOM, SRCVAR, and SRCSEQ trace ADT to its source: "DS", "DSDTC", and the disposition sequence number for a death event, or "RS", "RSDTC", and the response sequence number for a progression event or a censored assessment.

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

prompts/adam-adtte-pfs/conventions.md

Edit
16 lines

Following CDISC ADaM standards, use the provided ADSL, DS, and RS datasets to create an ADTTE dataset with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, SRCSEQ

PARAMCD is "PFS" and PARAM is "Progression-Free Survival" for every record. AVAL is the number of days from STARTDT through ADT, counting the randomization day as day one. CNSR is 0 or 1. EVNTDESC is "DISEASE PROGRESSION", "DEATH", or "CENSORED". SRCDOM is "DS" or "RS", or has no value. SRCVAR is "DSDTC" when SRCDOM is "DS" and "RSDTC" when SRCDOM is "RS", or has no value.

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

prompts/adam-adtte-pfs/brief.md

Edit
9 lines

Following CDISC ADaM standards, use the provided ADSL, DS, and RS datasets to create an ADTTE dataset with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, SRCSEQ

Read the source datasets from /app/input and save the completed dataset as /app/output/adtte.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/adam-adtte-pfs/result.R

Edit
116 lines
# Reference solution for the yamaa benchmark adam-adtte-pfs (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)adsl <- read_csv(  "/app/input/adsl.csv",  col_types = cols(    RANDDT = col_date(), NTXSTDT = col_date(), .default = col_character()  ))rs <- read_csv(  "/app/input/rs.csv",  col_types = cols(RSSEQ = col_integer(), RSDTC = col_date(), .default = col_character()))ds <- read_csv(  "/app/input/ds.csv",  col_types = cols(DSSEQ = col_integer(), DSDTC = col_date(), .default = col_character()))# The first adequate progression, the first death, and the last adequate# assessment. Records sharing a date are ordered by sequence number.progression <- rs |>  filter(RSTESTCD %in% "OVRLRESP", RSSTRESC %in% "PD", ADEQFL %in% "Y", !is.na(RSDTC)) |>  arrange(USUBJID, RSDTC, RSSEQ) |>  distinct(USUBJID, .keep_all = TRUE) |>  select(USUBJID, PDDT = RSDTC, PDSEQ = RSSEQ)death <- ds |>  filter(DSDECOD %in% "DEATH", !is.na(DSDTC)) |>  arrange(USUBJID, DSDTC, DSSEQ) |>  distinct(USUBJID, .keep_all = TRUE) |>  select(USUBJID, DTHDT = DSDTC, DTHSEQ = DSSEQ)last_assessment <- rs |>  filter(RSTESTCD %in% "OVRLRESP", ADEQFL %in% "Y", !is.na(RSDTC)) |>  arrange(USUBJID, desc(RSDTC), desc(RSSEQ)) |>  distinct(USUBJID, .keep_all = TRUE) |>  select(USUBJID, CENSORDT = RSDTC, LASTSEQ = RSSEQ)# The last adequate assessment dated on or before new-therapy start, for# subjects who began one.pre_therapy <- rs |>  inner_join(select(adsl, USUBJID, NTXSTDT), by = "USUBJID") |>  filter(    RSTESTCD %in% "OVRLRESP", ADEQFL %in% "Y", !is.na(RSDTC),    !is.na(NTXSTDT), RSDTC <= NTXSTDT  ) |>  arrange(USUBJID, desc(RSDTC), desc(RSSEQ)) |>  distinct(USUBJID, .keep_all = TRUE) |>  select(USUBJID, PRECENSORDT = RSDTC, PRESEQ = RSSEQ)adtte <- adsl |>  left_join(progression, by = "USUBJID") |>  left_join(death, by = "USUBJID") |>  left_join(last_assessment, by = "USUBJID") |>  left_join(pre_therapy, by = "USUBJID") |>  mutate(    PARAMCD = "PFS",    PARAM = "Progression-Free Survival",    STARTDT = RANDDT,    # A progression or death dated after new-therapy start is not an event.    PDDT = if_else(!is.na(NTXSTDT) & PDDT > NTXSTDT, as.Date(NA), PDDT),    DTHDT = if_else(!is.na(NTXSTDT) & DTHDT > NTXSTDT, as.Date(NA), DTHDT),    # The censoring assessment: the last adequate one overall, or the last    # one dated on or before therapy start when therapy began.    CENSORASSESSDT = if_else(is.na(NTXSTDT), CENSORDT, PRECENSORDT),    CENSORSEQ = if_else(is.na(NTXSTDT), LASTSEQ, PRESEQ),    # With no usable adequate assessment the subject is censored at    # randomization, for a 1-day PFS.    CENSORDT = coalesce(CENSORASSESSDT, STARTDT),    EVENTDT = case_when(      !is.na(PDDT) & !is.na(DTHDT) ~ pmin(PDDT, DTHDT),      !is.na(PDDT) ~ PDDT,      !is.na(DTHDT) ~ DTHDT    ),    # The event always wins, even after the last adequate assessment,    # unless new anti-cancer therapy started first.    CNSR = if_else(!is.na(EVENTDT), 0L, 1L),    ADT = coalesce(EVENTDT, CENSORDT),    AVAL = as.integer(ADT - STARTDT) + 1L,    # A progression and a death on the same day count as progression.    EVNTDESC = case_when(      is.na(EVENTDT) ~ "CENSORED",      !is.na(PDDT) & (is.na(DTHDT) | PDDT <= DTHDT) ~ "DISEASE PROGRESSION",      .default = "DEATH"    ),    SRCDOM = case_when(      EVNTDESC == "DEATH" ~ "DS",      is.na(CENSORASSESSDT) ~ NA_character_,      .default = "RS"    ),    SRCVAR = case_when(      EVNTDESC == "DEATH" ~ "DSDTC",      is.na(CENSORASSESSDT) ~ NA_character_,      .default = "RSDTC"    ),    SRCSEQ = case_when(      EVNTDESC == "DISEASE PROGRESSION" ~ PDSEQ,      EVNTDESC == "DEATH" ~ DTHSEQ,      is.na(CENSORASSESSDT) ~ NA_integer_,      .default = CENSORSEQ    )  ) |>  arrange(USUBJID) |>  select(    STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC,    SRCDOM, SRCVAR, SRCSEQ  )dir.create("/app/output", showWarnings = FALSE)write_csv(adtte, "/app/output/adtte.csv", na = "")

solutions/adam-adtte-pfs/result.py

Edit
155 lines
# Reference solution for the yamaa benchmark adam-adtte-pfs (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 ADTTE (PFS) from ADSL, DS, and RS per the task instruction.Reads /app/input/{adsl,ds,rs}.csv and writes /app/output/adtte.csv withone record per subject."""import csvfrom datetime import dateINPUT_DIR = "/app/input"OUTPUT_PATH = "/app/output/adtte.csv"def parse_date(text):    """Return a date for an ISO date string, or None when blank."""    text = (text or "").strip()    if not text:        return None    return date.fromisoformat(text)def read_rows(name):    with open(f"{INPUT_DIR}/{name}", newline="", encoding="utf-8") as handle:        return list(csv.DictReader(handle))def main():    adsl = read_rows("adsl.csv")    ds = read_rows("ds.csv")    rs = read_rows("rs.csv")    # Per-subject new anti-cancer therapy start: only dates on or before    # it are usable for events and censoring.    ntx_by_subject = {}    for subj in adsl:        key = (subj["STUDYID"], subj["USUBJID"])        ntx_by_subject[key] = parse_date(subj["NTXSTDT"])    def usable(key, day):        ntx = ntx_by_subject.get(key)        return ntx is None or day <= ntx    deaths = {}    for row in ds:        if row["DSDECOD"] != "DEATH":            continue        dthdt = parse_date(row["DSDTC"])        if dthdt is None:            continue        key = (row["STUDYID"], row["USUBJID"])        if not usable(key, dthdt):            continue        candidate = (dthdt, int(row["DSSEQ"]))        if key not in deaths or candidate < deaths[key]:            deaths[key] = candidate    progressions = {}    last_adequate = {}    for row in rs:        if row["RSTESTCD"] != "OVRLRESP" or row["ADEQFL"] != "Y":            continue        adt = parse_date(row["RSDTC"])        if adt is None:            continue        key = (row["STUDYID"], row["USUBJID"])        if not usable(key, adt):            continue        seq = int(row["RSSEQ"])        if row["RSSTRESC"] == "PD":            candidate = (adt, seq)            if key not in progressions or candidate < progressions[key]:                progressions[key] = candidate        latest = last_adequate.get(key)        if latest is None or (adt, seq) > latest:            last_adequate[key] = (adt, seq)    out_rows = []    for subj in adsl:        studyid = subj["STUDYID"]        usubjid = subj["USUBJID"]        key = (studyid, usubjid)        startdt = parse_date(subj["RANDDT"])        prog = progressions.get(key)        death = deaths.get(key)        srcdom = srcvar = srcseq = ""        if prog is not None and (death is None or prog[0] <= death[0]):            adt = prog[0]            cnsr = 0            evntdesc = "DISEASE PROGRESSION"            srcdom, srcvar, srcseq = "RS", "RSDTC", prog[1]        elif death is not None:            adt = death[0]            cnsr = 0            evntdesc = "DEATH"            srcdom, srcvar, srcseq = "DS", "DSDTC", death[1]        else:            cnsr = 1            evntdesc = "CENSORED"            last = last_adequate.get(key)            if last is None:                # No usable adequate assessment: censored at randomization.                adt = startdt            else:                adt = last[0]                srcdom, srcvar, srcseq = "RS", "RSDTC", last[1]        aval = (adt - startdt).days + 1        out_rows.append(            {                "STUDYID": studyid,                "USUBJID": usubjid,                "PARAMCD": "PFS",                "PARAM": "Progression-Free Survival",                "STARTDT": startdt.isoformat(),                "ADT": adt.isoformat(),                "AVAL": aval,                "CNSR": cnsr,                "EVNTDESC": evntdesc,                "SRCDOM": srcdom,                "SRCVAR": srcvar,                "SRCSEQ": srcseq,            }        )    fieldnames = [        "STUDYID",        "USUBJID",        "PARAMCD",        "PARAM",        "STARTDT",        "ADT",        "AVAL",        "CNSR",        "EVNTDESC",        "SRCDOM",        "SRCVAR",        "SRCSEQ",    ]    with open(OUTPUT_PATH, "w", newline="", encoding="utf-8") as handle:        writer = csv.DictWriter(handle, fieldnames=fieldnames)        writer.writeheader()        writer.writerows(out_rows)if __name__ == "__main__":    main()

solutions/adam-adtte-pfs/result.sas

Edit
52 lines
/* opensas reference solution for the yamaa benchmark adam-adtte-pfs.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data adsl;  length STUDYID USUBJID RANDDT NTXSTDT $1024;  infile "/app/input/adsl.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. RANDDT : $char1024. NTXSTDT : $char1024.;run;data ds;  length STUDYID USUBJID DSDTC DSDECOD $1024;  infile "/app/input/ds.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. DSSEQ DSDTC : $char1024. DSDECOD : $char1024.;run;data rs;  length STUDYID USUBJID RSTESTCD RSDTC RSSTRESC ADEQFL $1024;  infile "/app/input/rs.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. RSSEQ RSTESTCD : $char1024. RSDTC : $char1024. RSSTRESC : $char1024. ADEQFL : $char1024.;run;proc sql;  create table usable as select a.* from rs as a inner join adsl as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID where a.RSDTC ne '' and a.ADEQFL='Y' and a.RSTESTCD='OVRLRESP' and (missing(b.NTXSTDT) or a.RSDTC<=b.NTXSTDT);  create table progression as select a.* from rs as a inner join adsl as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID where a.RSDTC ne '' and a.RSSTRESC='PD' and a.ADEQFL='Y' and a.RSTESTCD='OVRLRESP' and (missing(b.NTXSTDT) or a.RSDTC<=b.NTXSTDT);  create table deaths as select a.* from ds as a inner join adsl as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID where a.DSDECOD='DEATH' and a.DSDTC ne '' and (missing(b.NTXSTDT) or a.DSDTC<=b.NTXSTDT);quit;proc sort data=progression; by STUDYID USUBJID RSDTC RSSEQ; run;data pd; set progression; by STUDYID USUBJID; if first.USUBJID; keep STUDYID USUBJID RSDTC RSSEQ; rename RSDTC=PDDT RSSEQ=PDSEQ; run;proc sort data=deaths; by STUDYID USUBJID DSDTC DSSEQ; run;data death; set deaths; by STUDYID USUBJID; if first.USUBJID; keep STUDYID USUBJID DSDTC DSSEQ; rename DSDTC=DEATHDT DSSEQ=DEATHSEQ; run;proc sort data=usable; by STUDYID USUBJID descending RSDTC descending RSSEQ; run;data lastassessment; set usable; by STUDYID USUBJID; if first.USUBJID; keep STUDYID USUBJID RSDTC RSSEQ; rename RSDTC=LASTDT RSSEQ=LASTSEQ; run;proc sql; create table joined as select a.*,b.PDDT,b.PDSEQ,c.DEATHDT,c.DEATHSEQ,d.LASTDT,d.LASTSEQ from adsl as a  left join pd as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID left join death as c on a.STUDYID=c.STUDYID and a.USUBJID=c.USUBJID left join lastassessment as d on a.STUDYID=d.STUDYID and a.USUBJID=d.USUBJID; quit;data result;  length PARAMCD $8 PARAM $40 STARTDT ADT $10 EVNTDESC CNSDTDSC $40 SRCDOM $4 SRCVAR $16;  set joined;  PARAMCD='PFS'; PARAM='Progression-Free Survival'; STARTDT=RANDDT;  if not missing(PDDT) and (missing(DEATHDT) or PDDT<=DEATHDT) then do; ADT=PDDT; CNSR=0; EVNTDESC='DISEASE PROGRESSION'; SRCDOM='RS'; SRCVAR='RSDTC'; SRCSEQ=PDSEQ; end;  else if not missing(DEATHDT) then do; ADT=DEATHDT; CNSR=0; EVNTDESC='DEATH'; SRCDOM='DS'; SRCVAR='DSDTC'; SRCSEQ=DEATHSEQ; end;  else do;    CNSR=1; EVNTDESC='CENSORED';    if not missing(LASTDT) then do; ADT=LASTDT; SRCDOM='RS'; SRCVAR='RSDTC'; SRCSEQ=LASTSEQ; end; else ADT=RANDDT;  end;  if not missing(ADT) and not missing(STARTDT) then AVAL=input(ADT,yymmdd10.)-input(STARTDT,yymmdd10.)+1;run;proc sql;  create table final as select STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, SRCSEQ from result;quit;proc export data=final outfile="/app/output/adtte.csv" dbms=csv replace; run;