Every Monday, charts the recent history of one metric from your sheet and emails you the chart with a note on what changed.

UsesClaudeGmailGoogle SheetsQuickChart

Weekly chart of how a metric has moved

ScheduleRun every Monday at 08:00.Run every Monday at 08:00.
ValuesSet spreadsheetUrl to blank.Set spreadsheetUrl to blank.
ValuesSet measureName to blank.Set measureName to blank.
ValuesSet recipientEmail to blank.Set recipientEmail to blank.
ValuesSet historyTab to History.Set historyTab to History.
ValuesSet maxPoints to 24.Set maxPoints to 24.
TextTrim measureName and save it as measureLabel.Trim measureName and save it as measureLabel.
TextMake measureLabel lowercase and save it as wantedMeasure.Make measureLabel lowercase and save it as wantedMeasure.
Google SheetsGoogle SheetsRead the rows of spreadsheetUrl. Save it as history.Read the rows of spreadsheetUrl. Save it as history.
ValuesSet periodLabels to an empty list.Set periodLabels to an empty list.
ValuesSet pointValues to an empty list.Set pointValues to an empty list.
ValuesSet readingLines to an empty list.Set readingLines to an empty list.
ValuesSet leftOff to an empty list.Set leftOff to an empty list.
Decisions & LoopsFor each entry in history.rows:For each entry in history.rows:
Decisions & LoopsIf a step below fails, save the error as rowErrorIf a step below fails, save the error as rowError
Try
TextMake entry.Measure lowercase and save it as rowMeasure.Make entry.Measure lowercase and save it as rowMeasure.
Decisions & LoopsIf rowMeasure is wantedMeasureIf rowMeasure is wantedMeasure
Yes
TextTrim entry.Period and save it as periodRaw.Trim entry.Period and save it as periodRaw.
TextTrim periodRaw with "Trim periodRaw with replaced by with , replaced by with replaced by and save it as period.
TextExtract the first number from entry.Value and save it as reading.Extract the first number from entry.Value and save it as reading.
Decisions & LoopsIf period is emptyIf period is empty
Yes
ListsAdd a row with no period name to the end of leftOff and save it as leftOff.Add a row with no period name to the end of leftOff and save it as leftOff.
Else if @reading is blank
ListsAdd period to the end of leftOff and save it as leftOff.Add period to the end of leftOff and save it as leftOff.
Otherwise
ListsAdd period to the end of periodLabels and save it as periodLabels.Add period to the end of periodLabels and save it as periodLabels.
ListsAdd reading as text to the end of pointValues and save it as pointValues.Add reading as text to the end of pointValues and save it as pointValues.
ListsAdd period: reading as text to the end of readingLines and save it as readingLines.Add period: reading as text to the end of readingLines and save it as readingLines.
No
No steps
On error
ValuesShow in the output: Skipped a row that could not be read: rowError as text.Show in the output: Skipped a row that could not be read: rowError as text.
ValuesSet cappedNote to blank.Set cappedNote to blank.
MathSet takeFromEnd to 0 minus maxPoints.Set takeFromEnd to 0 minus maxPoints.
Decisions & LoopsIf the length of periodLabels is greater than maxPointsIf the length of periodLabels is greater than maxPoints
Yes
ListsTake periodLabels from position takeFromEnd on and save it as periodLabels.Take periodLabels from position takeFromEnd on and save it as periodLabels.
ListsTake pointValues from position takeFromEnd on and save it as pointValues.Take pointValues from position takeFromEnd on and save it as pointValues.
ListsTake readingLines from position takeFromEnd on and save it as readingLines.Take readingLines from position takeFromEnd on and save it as readingLines.
ValuesChange cappedNote to Only the most recent maxPoints as text readings are charted. Raise 'maxPoints' to chart more, at the cost of a more crowded axis.Change cappedNote to Only the most recent maxPoints as text readings are charted. Raise 'maxPoints' to chart more, at the cost of a more crowded axis.
No
No steps
Decisions & LoopsIf periodLabels is emptyIf periodLabels is empty
Yes
GmailGmailSend an email to recipientEmail.Send an email to recipientEmail. Subject: No readings to chart for measureLabelNothing on the '@historyTab' tab had a readable number against the measure '@measureLabel', so there is no chart to send. Check that the measure is spelled the same way in the Measure column, and that whatever fills that tab is still running.
Decisions & LoopsFinish the run.Finish the run.
No
No steps
TextJoin periodLabels with , and save it as labelsText.Join periodLabels with , and save it as labelsText.
TextJoin pointValues with , and save it as valuesText.Join pointValues with , and save it as valuesText.
TextJoin readingLines with a new line and save it as numbersText.Join readingLines with a new line and save it as numbersText.
ValuesSet chartUrl to blank.Set chartUrl to blank.
Decisions & LoopsIf a step below fails, save the error as chartErrorIf a step below fails, save the error as chartError
Try
QuickChartQuickChartCreate a line chart titled measureLabel. Save it as chart.Create a line chart titled measureLabel. Save it as chart.
TextTrim chart.url and save it as chartUrl.Trim chart.url and save it as chartUrl.
On error
ValuesShow in the output: The chart could not be drawn: chartError as text.Show in the output: The chart could not be drawn: chartError as text.
ValuesSet note to blank.Set note to blank.
Decisions & LoopsIf a step below fails, save the error as noteErrorIf a step below fails, save the error as noteError
Try
ClaudeClaudeAsk Claude: 'Below is one measure taken from a spreadsheet: one reading per period, oldest first. Write two or three sentences describing how it has moved.Ask Claude: 'Below is one measure taken from a spreadsheet: one reading per period, oldest first. Write two or three sentences describing how it has moved. Rules you must follow: - Describe only what these readings show. Do not forecast, project, extrapolate or say what is likely to happen next. - Do not suggest a target, a goal or an action, and do not say whether a number is good or bad. - Do not explain WHY anything moved. A list of readings cannot tell you that, and a plausible invented cause is worse than none. - Use the readings exactly as given. Do not invent readings for periods that are not listed. - Say where the highest and lowest readings fall, and whether the most recent stretch moves differently from the earlier part of the run. Measure: measureLabel Readings: numbersText'. Save it as written. Maximum response length: 400
TextTrim written.response_text and save it as note.Trim written.response_text and save it as note.
On error
ValuesShow in the output: The note on what changed could not be written: noteError as text.Show in the output: The note on what changed could not be written: noteError as text.
ValuesSet bodyParts to an empty list.Set bodyParts to an empty list.
ListsAdd Here is how '@measureLabel' has moved over its last the length of periodLabels as text readings. to the end of bodyParts and save it as bodyParts.Add Here is how '@measureLabel' has moved over its last the length of periodLabels as text readings. to the end of bodyParts and save it as bodyParts.
Decisions & LoopsIf note isn't emptyIf note isn't empty
Yes
ListsAdd What the readings show:Add What the readings show: note to the end of bodyParts and save it as bodyParts.
No
No steps
Decisions & LoopsIf chartUrl is emptyIf chartUrl is empty
Yes
ListsAdd The chart could not be drawn this time, so the readings are below on their own. to the end of bodyParts and save it as bodyParts.Add The chart could not be drawn this time, so the readings are below on their own. to the end of bodyParts and save it as bodyParts.
No
ListsAdd The chart:Add The chart: chartUrl to the end of bodyParts and save it as bodyParts.
ListsAdd The readings it was drawn from:Add The readings it was drawn from: numbersText to the end of bodyParts and save it as bodyParts.
Decisions & LoopsIf cappedNote isn't emptyIf cappedNote isn't empty
Yes
ListsAdd cappedNote to the end of bodyParts and save it as bodyParts.Add cappedNote to the end of bodyParts and save it as bodyParts.
No
No steps
Decisions & LoopsIf leftOff isn't emptyIf leftOff isn't empty
Yes
ListsAdd Left off the chart, with no readable number: leftOff joined by , to the end of bodyParts and save it as bodyParts.Add Left off the chart, with no readable number: leftOff joined by , to the end of bodyParts and save it as bodyParts.
No
No steps
TextJoin bodyParts with a blank line and save it as emailBody.Join bodyParts with a blank line and save it as emailBody.
GmailGmailSend an email to recipientEmail.Send an email to recipientEmail. Subject: measureLabel - how it has movedemailBody