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: derive FASEQ, FATESTCD, FATEST, FAOBJ, FACAT,
FAORRES, FAORRESU, FASTRESC, FASTRESN, FASTRESU,
FALNKID, and FADTC for findings collected about individual
adverse events on a supplementary form, in Findings About (FA).
Input: long-form Operational Data Model (ODM) item data: one row per
collected item, identified by study (StudyOID), subject (SubjectKey),
event (StudyEventOID plus its repeat key), item group, and item
(ItemOID), with the collected answer in Value. Each adverse event is one
event occurrence, and the findings collected about it ride on the same
occurrence. The findings form's IT.FA.AELNKID item carries the link
identifier shared with the adverse event record, whose term is the
IT.AE.AETERM item on that same occurrence. The study and subject
identifiers come straight from the ODM rows; no demographics input is
needed.
Variables:
FASEQnumbers the subject's records by linked event, then by test order (LOC,SIZE,BIOPSY); the collected result breaks any remaining ties so the numbering is fully determined.FATESTCDisLOCfor the location record,SIZEfor the size record, andBIOPSYfor the biopsy record. A test with no answer on the form has no record.FATESTisLocation,Size, andBiopsiedrespectively.FAOBJis the event term of the linked adverse event, read from theIT.AE.AETERMitem on the same event occurrence. Every emitted finding must carry that term; an event with no findings needs none.FACATis alwaysAE.FAORRESis the answer as recorded on the form.FAORRESUis the size unit, onSIZErecords only.FASTRESCcopiesFAORRES.FASTRESNis the measured size as a number, onSIZErecords only.FASTRESUis the size unit, onSIZErecords only.FALNKIDcarries the link identifier shared with the linked adverse event record, tying each finding to its specific event.FADTCis the date the findings form was collected.
Note: an adverse event with no findings form has no FA records. Two
events can share the same term, so FAOBJ alone does not distinguish them;
FALNKID is what ties each finding to its specific event record. A finding
whose event term is absent stops the run.
Inputs
Under review1 source fileinput/odm.csv
Edit19 rows| StudyOID | MetaDataVersionOID | SubjectKey | StudyEventOID | StudyEventRepeatKey | ItemGroupOID | ItemGroupRepeatKey | ItemOID | Value |
|---|---|---|---|---|---|---|---|---|
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.AE | 1 | IT.AE.AETERM | Rash |
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.AE | 1 | IT.AE.AELNKID | AE-1 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.AE | 1 | IT.AE.AEDTC | 2025-04-01 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.FA | 1 | IT.FA.AELNKID | AE-1 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.FA | 1 | IT.FA.LOCATION | LEFT FOREARM |
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.FA | 1 | IT.FA.SIZE | 25 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.FA | 1 | IT.FA.SIZEU | mm |
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.FA | 1 | IT.FA.BIOPSY | Y |
| RASH01 | MDV.1 | RASH01-2001 | AE | 1 | IG.FA | 1 | IT.FA.FADTC | 2025-04-02 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.AE | 1 | IT.AE.AETERM | Rash |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.AE | 1 | IT.AE.AELNKID | AE-2 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.AE | 1 | IT.AE.AEDTC | 2025-05-10 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.FA | 1 | IT.FA.AELNKID | AE-2 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.FA | 1 | IT.FA.LOCATION | RIGHT LEG |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.FA | 1 | IT.FA.SIZE | 40 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.FA | 1 | IT.FA.SIZEU | mm |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.FA | 1 | IT.FA.BIOPSY | N |
| RASH01 | MDV.1 | RASH01-2001 | AE | 2 | IG.FA | 1 | IT.FA.FADTC | 2025-05-11 |
| RASH01 | MDV.1 | RASH01-2001 | AE | 3 | IG.AE | 1 | IT.AE.AEDTC | 2025-04-05 |
Expected output
Under review6 expected rowsexpected/fa.csv
Edit6 rows| DOMAIN | STUDYID | USUBJID | FASEQ | FATESTCD | FATEST | FAOBJ | FACAT | FAORRES | FAORRESU | FASTRESC | FASTRESN | FASTRESU | FALNKID | FADTC |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| FA | RASH01 | 1 | LOC | Location | Rash | AE | LEFT FOREARM | — | LEFT FOREARM | — | — | AE-1 | 2025-04-02 | |
| FA | RASH01 | 2 | SIZE | Size | Rash | AE | 25 | mm | 25 | 25 | mm | AE-1 | 2025-04-02 | |
| FA | RASH01 | 3 | BIOPSY | Biopsied | Rash | AE | Y | — | Y | — | — | AE-1 | 2025-04-02 | |
| FA | RASH01 | 4 | LOC | Location | Rash | AE | RIGHT LEG | — | RIGHT LEG | — | — | AE-2 | 2025-05-11 | |
| FA | RASH01 | 5 | SIZE | Size | Rash | AE | 40 | mm | 40 | 40 | mm | AE-2 | 2025-05-11 | |
| FA | RASH01 | 6 | BIOPSY | Biopsied | Rash | AE | N | — | N | — | — | AE-2 | 2025-05-11 |
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 "FA". | 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 | ||||
| FATESTCD | Findings About Test Short Name | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 4 | ||||
| FATEST | Findings About Test Name | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 5 | ||||
| FAOBJ | Object of the Observation | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 6 | ||||
| FACAT | Category for Findings About | Char | Assigned | Set constant value "AE". | SDTM | 7 | ||||
| FAORRES | Result or Finding in Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 8 | ||||
| FAORRESU | Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 9 | ||||
| FASTRESC | Character Result/Finding in Std Format | Char | Derived | Copy value from FAORRES. | SDTM | 10 | ||||
| FASTRESN | Numeric Result/Finding in Standard Units | Num | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 11 | ||||
| FASTRESU | Standard Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 12 | ||||
| FALNKID | Link Identifier | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 13 | ||||
| FADTC | Date/Time of Collection | Char | ISO 8601 | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 14 | |||
| FASEQ | Sequence Number | Num | Derived | Row number within each group of STUDYID, USUBJID, ordered by FALNKID then TESTORD then FAORRES. | SDTM | 15 |
| Template | Include when | FATESTCD | FATEST | FAOBJ | FAORRES | FAORRESU | FASTRESN | FASTRESU | FALNKID | FADTC | FASTRESC |
|---|---|---|---|---|---|---|---|---|---|---|---|
| location | FAORRES IS NOT NULL | Constant "LOC" | Constant "Location" | Copy Value from the ODM_AE record whose StudyOID equals ODM.StudyOID, SubjectKey equals ODM.SubjectKey, StudyEventOID equals ODM.StudyEventOID and StudyEventRepeatKey equals ODM.StudyEventRepeatKey, where ODM_AE.ItemOID = 'IT.AE.AETERM', and no match yields blank | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.LOCATION' | Constant "None" | Constant "None" | Constant "None" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.AELNKID' | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.FADTC' | None |
| size | FAORRES IS NOT NULL | Constant "SIZE" | Constant "Size" | Copy Value from the ODM_AE record whose StudyOID equals ODM.StudyOID, SubjectKey equals ODM.SubjectKey, StudyEventOID equals ODM.StudyEventOID and StudyEventRepeatKey equals ODM.StudyEventRepeatKey, where ODM_AE.ItemOID = 'IT.AE.AETERM', and no match yields blank | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.SIZE' | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.SIZEU' | Copy FAORRES | Copy FAORRESU | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.AELNKID' | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.FADTC' | Copy FAORRES |
| biopsy | FAORRES IS NOT NULL | Constant "BIOPSY" | Constant "Biopsied" | Copy Value from the ODM_AE record whose StudyOID equals ODM.StudyOID, SubjectKey equals ODM.SubjectKey, StudyEventOID equals ODM.StudyEventOID and StudyEventRepeatKey equals ODM.StudyEventRepeatKey, where ODM_AE.ItemOID = 'IT.AE.AETERM', and no match yields blank | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.BIOPSY' | Constant "None" | Constant "None" | Constant "None" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.AELNKID' | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.FA.FADTC' | None |
| 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-fa-event-findings/full.md
Edit32 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an FA dataset with one record per finding about an adverse event.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, FASEQ, FATESTCD, FATEST, FAOBJ, FACAT, FAORRES, FAORRESU, FASTRESC, FASTRESN, FASTRESU, FALNKID, FADTC
DOMAIN is "FA". STUDYID is the ODM study OID and USUBJID is the ODM subject key, each as given. Each adverse event occurrence with a findings form gives one record for each test the form answers, and a test with no answer on the form has no record. FATESTCD is "LOC" for the location record, "SIZE" for the size record, and "BIOPSY" for the biopsy record. FATEST is "Location" for a location record, "Size" for a size record, and "Biopsied" for a biopsy record. FAOBJ is the term of the linked adverse event, read from the IT.AE.AETERM item on the same event occurrence. FACAT is "AE" on every record. FAORRES is the answer as recorded on the form. FAORRESU is the size unit on "SIZE" records and has no value on "LOC" and "BIOPSY" records. FASTRESC holds the same value as FAORRES. FASTRESN is the measured size as a number on "SIZE" records and has no value on "LOC" and "BIOPSY" records. FASTRESU is the size unit on "SIZE" records and has no value on "LOC" and "BIOPSY" records. FALNKID is the value of the IT.FA.AELNKID item on the findings form, the link identifier shared with the linked adverse event record. Two adverse events can share the same term, so FALNKID, not FAOBJ, ties each finding to its specific event. FADTC is the date the findings form was collected. An adverse event with no findings form has no records. FASEQ numbers each subject's records from 1 by linked event, then by test order "LOC", "SIZE", "BIOPSY", and the collected result breaks any remaining ties.
Read the source datasets from /app/input and save the completed dataset as /app/output/fa.csv.
prompts/sdtm-fa-event-findings/conventions.md
Edit14 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an FA dataset with one record per finding about an adverse event.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, FASEQ, FATESTCD, FATEST, FAOBJ, FACAT, FAORRES, FAORRESU, FASTRESC, FASTRESN, FASTRESU, FALNKID, FADTC
DOMAIN is "FA". FATESTCD is "LOC" for the location record, "SIZE" for the size record, and "BIOPSY" for the biopsy record. FATEST is "Location" for a location record, "Size" for a size record, and "Biopsied" for a biopsy record. FACAT is "AE" on every record.
Read the source datasets from /app/input and save the completed dataset as /app/output/fa.csv.
prompts/sdtm-fa-event-findings/brief.md
Edit9 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an FA dataset with one record per finding about an adverse event.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, FASEQ, FATESTCD, FATEST, FAOBJ, FACAT, FAORRES, FAORRESU, FASTRESC, FASTRESN, FASTRESU, FALNKID, FADTC
Read the source datasets from /app/input and save the completed dataset as /app/output/fa.csv.
R
Edit3 lineslibrary(yamaa)yamaa_spec("spec.yaml")$run()$outputPython
Edit3 linesimport yamaayamaa.spec("spec.yaml").run().outputsolutions/sdtm-fa-event-findings/result.R
Edit102 lines# Reference solution for the yamaa benchmark sdtm-fa-event-findings (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)library(purrr)odm <- read_csv( "/app/input/odm.csv", col_types = cols(.default = col_character()))long <- odm |> mutate(Value = na_if(Value, ""))ae <- long |> filter(ItemGroupOID == "IG.AE") |> select(SubjectKey, StudyOID, StudyEventOID, StudyEventRepeatKey, ItemOID, Value) |> pivot_wider(names_from = ItemOID, values_from = Value) |> rename( AETERM = `IT.AE.AETERM`, AELNKID_AE = `IT.AE.AELNKID` )fa_forms <- long |> filter(ItemGroupOID == "IG.FA") |> select( SubjectKey, StudyOID, StudyEventOID, StudyEventRepeatKey, ItemGroupRepeatKey, ItemOID, Value ) |> pivot_wider(names_from = ItemOID, values_from = Value) |> rename( LINK_FA = `IT.FA.AELNKID`, LOCATION = `IT.FA.LOCATION`, SIZE = `IT.FA.SIZE`, SIZEU = `IT.FA.SIZEU`, BIOPSY = `IT.FA.BIOPSY`, FADTC = `IT.FA.FADTC` )fa <- fa_forms |> left_join( ae, by = c("SubjectKey", "StudyEventOID", "StudyEventRepeatKey") ) |> filter(!is.na(AETERM) & AETERM != "") |> mutate( LINK = coalesce(LINK_FA, AELNKID_AE), STUDYID = StudyOID.x, USUBJID = SubjectKey ) |> select(STUDYID, USUBJID, LINK, FADTC, SIZEU, AETERM, LOCATION, SIZE, BIOPSY) |> pivot_longer( cols = c(LOCATION, SIZE, BIOPSY), names_to = "WHICH", values_to = "FAORRES" ) |> filter(!is.na(FAORRES)) |> mutate( ORDER = case_when( WHICH == "LOCATION" ~ 0L, WHICH == "SIZE" ~ 1L, .default = 2L ), FATESTCD = case_when( WHICH == "LOCATION" ~ "LOC", WHICH == "SIZE" ~ "SIZE", .default = "BIOPSY" ), FATEST = case_when( WHICH == "LOCATION" ~ "Location", WHICH == "SIZE" ~ "Size", .default = "Biopsied" ), FAOBJ = AETERM, FACAT = "AE", IS_SIZE = WHICH == "SIZE", FAORRESU = if_else(IS_SIZE, SIZEU, NA_character_), FASTRESC = FAORRES, FASTRESN = if_else( IS_SIZE, suppressWarnings(as.numeric(FAORRES)), NA_real_ ), FASTRESU = if_else(IS_SIZE, SIZEU, NA_character_), FALNKID = LINK, DOMAIN = "FA", TIE = FAORRES ) |> arrange(STUDYID, USUBJID, LINK, ORDER, TIE) |> group_by(STUDYID, USUBJID) |> mutate(FASEQ = row_number()) |> ungroup() |> select( DOMAIN, STUDYID, USUBJID, FASEQ, FATESTCD, FATEST, FAOBJ, FACAT, FAORRES, FAORRESU, FASTRESC, FASTRESN, FASTRESU, FALNKID, FADTC )dir.create("/app/output", showWarnings = FALSE)write_csv(fa, "/app/output/fa.csv", na = "")solutions/sdtm-fa-event-findings/result.py
Edit170 lines# Reference solution for the yamaa benchmark sdtm-fa-event-findings (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 FA dataset (findings about adverse events) from ODM data.Reads the long-format ODM extract from /app/input and writes one FArecord per answered test (LOC, SIZE, BIOPSY) on each findings formlinked to an adverse event occurrence, to /app/output/fa.csv."""import csvimport osINPUT_DIR = "/app/input"OUTPUT_DIR = "/app/output"COLUMNS = [ "DOMAIN", "STUDYID", "USUBJID", "FASEQ", "FATESTCD", "FATEST", "FAOBJ", "FACAT", "FAORRES", "FAORRESU", "FASTRESC", "FASTRESN", "FASTRESU", "FALNKID", "FADTC",]TEST_ORDER = {"LOC": 0, "SIZE": 1, "BIOPSY": 2}TEST_NAMES = {"LOC": "Location", "SIZE": "Size", "BIOPSY": "Biopsied"}def read_odm(input_dir): """Return the rows of the ODM long-format extract in /app/input.""" for name in sorted(os.listdir(input_dir)): if not name.lower().endswith(".csv"): continue path = os.path.join(input_dir, name) with open(path, newline="", encoding="utf-8") as handle: reader = csv.DictReader(handle) fields = reader.fieldnames or [] if {"StudyOID", "SubjectKey", "ItemOID", "Value"} <= set(fields): return list(reader) raise FileNotFoundError("No ODM extract found in " + input_dir)def group_forms(rows): """Group ODM rows into form occurrences keyed by their identity. Returns (ae_forms, fa_forms): lists of dicts with the occurrence keys and an ``items`` mapping of ItemOID to collected value. """ forms = {} order = [] for row in rows: key = ( row["StudyOID"], row["SubjectKey"], row["StudyEventOID"], row["StudyEventRepeatKey"], row["ItemGroupOID"], row["ItemGroupRepeatKey"], ) if key not in forms: forms[key] = { "study": row["StudyOID"], "subject": row["SubjectKey"], "event": row["StudyEventOID"], "event_repeat": row["StudyEventRepeatKey"], "group": row["ItemGroupOID"], "group_repeat": row["ItemGroupRepeatKey"], "items": {}, "position": len(order), } order.append(key) forms[key]["items"][row["ItemOID"]] = row["Value"] ae_forms = [forms[k] for k in order if forms[k]["group"] == "IG.AE"] fa_forms = [forms[k] for k in order if forms[k]["group"] == "IG.FA"] return ae_forms, fa_formsdef as_number(text): """Format a collected measurement as a plain number string.""" value = float(text) if value == int(value): return str(int(value)) return str(value)def main(): rows = read_odm(INPUT_DIR) ae_forms, fa_forms = group_forms(rows) # Index AE occurrences by subject and event occurrence so each # findings form can read the term of its linked event. ae_by_event = {} for form in ae_forms: key = (form["study"], form["subject"], form["event"], form["event_repeat"]) ae_by_event[key] = form records = [] for form in fa_forms: items = form["items"] lnkid = items.get("IT.FA.AELNKID", "") ae_key = (form["study"], form["subject"], form["event"], form["event_repeat"]) ae_form = ae_by_event.get(ae_key) faobj = ae_form["items"].get("IT.AE.AETERM", "") if ae_form else "" event_order = ae_form["position"] if ae_form else form["position"] fadtc = items.get("IT.FA.FADTC", "") size_unit = items.get("IT.FA.SIZEU", "") answers = [] location = items.get("IT.FA.LOCATION", "") if location != "": answers.append(("LOC", location, "", "", "")) size = items.get("IT.FA.SIZE", "") if size != "": answers.append(("SIZE", size, size_unit, as_number(size), size_unit)) biopsy = items.get("IT.FA.BIOPSY", "") if biopsy != "": answers.append(("BIOPSY", biopsy, "", "", "")) for testcd, orres, orresu, stresn, stresu in answers: records.append({ "subject": form["subject"], "event_order": event_order, "test_order": TEST_ORDER[testcd], "tiebreak": orres, "row": { "DOMAIN": "FA", "STUDYID": form["study"], "USUBJID": form["subject"], "FASEQ": "", "FATESTCD": testcd, "FATEST": TEST_NAMES[testcd], "FAOBJ": faobj, "FACAT": "AE", "FAORRES": orres, "FAORRESU": orresu, "FASTRESC": orres, "FASTRESN": stresn, "FASTRESU": stresu, "FALNKID": lnkid, "FADTC": fadtc, }, }) # Subject order follows first appearance in the collected data. subject_order = {} for row in rows: if row["SubjectKey"] not in subject_order: subject_order[row["SubjectKey"]] = len(subject_order) records.sort(key=lambda r: (subject_order[r["subject"]], r["event_order"], r["test_order"], r["tiebreak"])) counters = {} for record in records: subject = record["subject"] counters[subject] = counters.get(subject, 0) + 1 record["row"]["FASEQ"] = str(counters[subject]) os.makedirs(OUTPUT_DIR, exist_ok=True) out_path = os.path.join(OUTPUT_DIR, "fa.csv") with open(out_path, "w", newline="", encoding="utf-8") as handle: writer = csv.DictWriter(handle, fieldnames=COLUMNS) writer.writeheader() for record in records: writer.writerow(record["row"])if __name__ == "__main__": main()solutions/sdtm-fa-event-findings/result.sas
Edit59 lines/* opensas reference solution for the yamaa benchmark sdtm-fa-event-findings. 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 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. ItemGroupOID : $char1024. ItemGroupRepeatKey : $char1024. ItemOID : $char1024. Value : $char1024.;run;proc sort data=odm out=ordered; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey ItemGroupRepeatKey; run;data events; length FAOBJ AELNKID $1024; retain FAOBJ AELNKID; set ordered; where ItemGroupOID='IG.AE'; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey ItemGroupRepeatKey; if first.ItemGroupRepeatKey then do; FAOBJ=''; AELNKID=''; end; if ItemOID='IT.AE.AETERM' then FAOBJ=Value; if ItemOID='IT.AE.AELNKID' then AELNKID=Value; if last.ItemGroupRepeatKey then output;run;proc sort data=odm out=ordered; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey ItemGroupRepeatKey; run;data findings; length FALNKID LOC SIZE SIZEU BIOPSY FADTC $1024; retain FALNKID LOC SIZE SIZEU BIOPSY FADTC; set ordered; where ItemGroupOID='IG.FA'; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey ItemGroupRepeatKey; if first.ItemGroupRepeatKey then do; FALNKID=''; LOC=''; SIZE=''; SIZEU=''; BIOPSY=''; FADTC=''; end; if ItemOID='IT.FA.AELNKID' then FALNKID=Value; if ItemOID='IT.FA.LOCATION' then LOC=Value; if ItemOID='IT.FA.SIZE' then SIZE=Value; if ItemOID='IT.FA.SIZEU' then SIZEU=Value; if ItemOID='IT.FA.BIOPSY' then BIOPSY=Value; if ItemOID='IT.FA.FADTC' then FADTC=Value; if last.ItemGroupRepeatKey then output;run;proc sql;create table joined as select a.*,b.FAOBJ from findings a inner join events b on a.StudyOID=b.StudyOID and a.SubjectKey=b.SubjectKey and a.FALNKID=b.AELNKID; quit;data prepared; set joined; length DOMAIN $2 STUDYID USUBJID FATESTCD FATEST FACAT FAORRES FAORRESU FASTRESC FASTRESU $1024;DOMAIN='FA'; STUDYID=StudyOID; USUBJID=SubjectKey; FACAT='AE'; FAORRESU=''; FASTRESU=''; FASTRESN=.; EVENTREP=input(ItemGroupRepeatKey,best32.);if not missing(LOC) then do; FATESTCD='LOC'; FATEST='Location'; FAORRES=LOC; FASTRESC=FAORRES; TESTORD=1; output; end;if not missing(SIZE) then do; FATESTCD='SIZE'; FATEST='Size'; FAORRES=SIZE; FASTRESC=FAORRES; FAORRESU=SIZEU; FASTRESU=SIZEU; FASTRESN=input(SIZE,best32.); TESTORD=2; output; end;if not missing(BIOPSY) then do; FATESTCD='BIOPSY'; FATEST='Biopsied'; FAORRES=BIOPSY; FASTRESC=FAORRES; FAORRESU=''; FASTRESU=''; FASTRESN=.; TESTORD=3; output; end; run;proc sort data=prepared; by STUDYID USUBJID FALNKID TESTORD FAORRES; run;data result; set prepared; by STUDYID USUBJID; retain FASEQ; if first.USUBJID then FASEQ=0; FASEQ+1;run;proc sql; create table final as select DOMAIN, STUDYID, USUBJID, FASEQ, FATESTCD, FATEST, FAOBJ, FACAT, FAORRES, FAORRESU, FASTRESC, FASTRESN, FASTRESU, FALNKID, FADTC from result;quit;proc export data=final outfile="/app/output/fa.csv" dbms=csv replace; run;
yamaa