Cleans a research dataset by fixed rules and writes down every change, exclusion and unresolved value as it makes it.
Uses
Claude
Google Sheets
Research data cleaning with a decision log
spreadsheetUrl to blank.Set spreadsheetUrl to blank.rawTab to Raw.Set rawTab to Raw.cleanTab to Clean.Set cleanTab to Clean.logTab to Cleaning log.Set logTab to Cleaning log.reviewTab to Needs review.Set reviewTab to Needs review.runsTab to Runs.Set runsTab to Runs.rulesetVersion to v1.Set rulesetVersion to v1.missingToken to NA.Set missingToken to NA.maxRows to 500.Set maxRows to 500.batchSize to 100.Set batchSize to 100.writeMethodsNote to true.Set writeMethodsNote to true.columnRules to {participant_id:{type:id,pattern:^P[0-9]{4}$,required:true},submitted_at:{type:datetime,formats:["%.Set columnRules to {participant_id:{type:id,pattern:^P[0-9]{4}$,required:true},submitted_at:{type:datetime,formats:["%.rowRules to {consentColumn:consent,duplicateKey:[participant_id],duplicateKeep:first,deduplicateWholeRows:true,"atten.Set rowRules to {consentColumn:consent,duplicateKey:[participant_id],duplicateKeep:first,deduplicateWholeRows:true,"atten.runId.Generate a short ID code and save it as runId.stampedAt.Get the current date and time and save it as stampedAt.spreadsheetUrl. Save it as raw.Read the rows of spreadsheetUrl. Save it as raw.rows to raw.rows.Set rows to raw.rows.headers to raw.headers.Set headers to raw.headers.rows and save it as totalRows.Count the items in rows and save it as totalRows.totalRows is 0If totalRows is 0Yes
No
No steps
totalRows is greater than maxRowsIf totalRows is greater than maxRowsYes
No
No steps
rows as text and save it as payloadChars.Count the characters in rows as text and save it as payloadChars.payloadChars is greater than 700000If payloadChars is greater than 700000Yes
No
No steps
config to a record of missingToken, columnRules, and rowRules.Set config to a record of missingToken, columnRules, and rowRules.cleanRows to an empty list.Set cleanRows to an empty list.logRows to an empty list.Set logRows to an empty list.reviewRows to an empty list.Set reviewRows to an empty list.configErrors to an empty list.Set configErrors to an empty list.fatal to blank.Set fatal to blank.offset to 0.Set offset to 0.offset is less than totalRows:While offset is less than totalRows:rows, headers, config, window, run_id, ruleset_version, and stamped_at and save it as batch.Run Python code on rows, headers, config, window, run_id, ruleset_version, and stamped_at and save it as batch.batch.fatal isn't emptyIf batch.fatal isn't emptyYes
fatal to batch.fatal.Change fatal to batch.fatal.No
No steps
cleanRows and batch.clean into one list and save it as cleanRows.Combine cleanRows and batch.clean into one list and save it as cleanRows.logRows and batch.log into one list and save it as logRows.Combine logRows and batch.log into one list and save it as logRows.reviewRows and batch.review into one list and save it as reviewRows.Combine reviewRows and batch.review into one list and save it as reviewRows.configErrors to batch.config_errors.Change configErrors to batch.config_errors.offset to offset plus batchSize.Change offset to offset plus batchSize.fatal isn't emptyIf fatal isn't emptyYes
fatal.Stop and report a problem: fatal.No
No steps
clean_count, log, review, total, and config_errors and save it as summary.Run Python code on clean_count, log, review, total, and config_errors and save it as summary.tabName in [cleanTab, logTab, reviewTab]:For each tabName in [cleanTab, logTab, reviewTab]:tabErrorIf a step below fails, save the error as tabErrorTry
tabName in spreadsheetUrl.Create the tab tabName in spreadsheetUrl.On error
tabName.Show in the output: Reusing the existing tab: tabName.runsTabErrorIf a step below fails, save the error as runsTabErrorTry
runsTab in spreadsheetUrl.Create the tab runsTab in spreadsheetUrl.runsTab in spreadsheetUrl.Add a row to runsTab in spreadsheetUrl.On error
Appending to the existing Runs tab..Show in the output: Appending to the existing Runs tab..outputTabs to [a record of tab, header, and rows, a record of tab, header, and rows, a record of tab, header, and rows].Set outputTabs to [a record of tab, header, and rows, a record of tab, header, and rows, a record of tab, header, and rows].writingTab to blank.Set writingTab to blank.rowsWritten to 0.Set rowsWritten to 0.writeErrorIf a step below fails, save the error as writeErrorTry
output in outputTabs:For each output in outputTabs:writingTab to output.tab. Change writingTab to output.tab.rowsWritten to 0. Change rowsWritten to 0.output.tab in spreadsheetUrl. Clear the tab output.tab in spreadsheetUrl.output.tab in spreadsheetUrl. Add a row to output.tab in spreadsheetUrl.outputRow in output.rows: For each outputRow in output.rows:output.tab in spreadsheetUrl. Add a row to output.tab in spreadsheetUrl.rowsWritten. Add 1 to rowsWritten.On error
incompleteNote to INCOMPLETE: run runId cleared this tab and then failed after writing rowsWritten as text data rows (writeError.message). Run the workflow again to rebuild it from the raw tab.Set incompleteNote to INCOMPLETE: run runId cleared this tab and then failed after writing rowsWritten as text data rows (writeError.message). Run the workflow again to rebuild it from the raw tab.markerErrorIf a step below fails, save the error as markerErrorTry
writingTab in spreadsheetUrl.Add a row to writingTab in spreadsheetUrl.On error
markerError.message.Show in the output: Could not mark the '@writingTab' tab as incomplete: markerError.message.rowsWritten as text data rows: writeError.message. The Clean, Cleaning log and Needs review tabs are rebuilt in that order on every run, so tabs rebuilt before this one hold this run's output, tabs after it still hold the previous run's, and '@writingTab' is marked INCOMPLETE in its last row. Nothing was added to the Runs tab and the raw tab was not touched. Fix the cause and run again: a full run rebuilds all three tabs.Stop and report a problem: Writing the '@writingTab' tab failed after rowsWritten as text data rows: writeError.message. The Clean, Cleaning log and Needs review tabs are rebuilt in that order on every run, so tabs rebuilt before this one hold this run's output, tabs after it still hold the previous run's, and '@writingTab' is marked INCOMPLETE in its last row. Nothing was added to the Runs tab and the raw tab was not touched. Fix the cause and run again: a full run rebuilds all three tabs.methodsNote to blank.Set methodsNote to blank.writeMethodsNote is yesIf writeMethodsNote is yesYes
noteErrorIf a step below fails, save the error as noteErrorTry
summary as text
Ruleset version: rulesetVersion
Write one paragraph, 80-120 words, past tense, third person, no bullet points and no heading. State how many records were read, how many were excluded and under which rules, how many values were changed and how many were left for manual review. Name the ruleset version. Describe only what these numbers say: do not judge the data quality, do not speculate about causes, do not recommend anything, and do not mention any figure that is not above. If reconciles is false, say plainly that the participant counts do not reconcile and that the log needs checking.'. Save it as note.
Maximum response length: 700note.response_text and save it as methodsNote.Trim note.response_text and save it as methodsNote.On error
methodsNote to The methods note could not be written; the cleaned data and the log are unaffected. Reason: noteError.message.Change methodsNote to The methods note could not be written; the cleaned data and the log are unaffected. Reason: noteError.message.No
No steps
runsTab in spreadsheetUrl.Add a row to runsTab in spreadsheetUrl.summary.Show in the output: summary.