Every Monday, charts the recent history of one metric from your sheet and emails you the chart with a note on what changed.
Uses
Claude
Gmail
Google SheetsQuickChart
Weekly chart of how a metric has moved
Every Monday at 08:00, the measurement history you keep in Google Sheets is read, the measure you name is drawn as a line chart of its most recent readings, and the chart is emailed to you with a short note from the AI on how the numbers have moved.
The note describes only what the readings show: no forecast, no target, no advice and no explanation of why anything moved, because a list of numbers cannot tell you that. The readings are printed underneath, so nothing in the note has to be taken on trust. A value can be written with currency signs, commas or percentages and still read fine, and a row with no readable number is listed as left off rather than counted as zero. If the chart cannot be drawn the readings are still emailed, and if nothing matches the measure you get an email saying so.
The tab needs one row per reading with 'Period', 'Measure' and 'Value' columns, oldest at the top. The email carries a permanent link to the chart image rather than an attachment.
spreadsheetUrl to blank.Set spreadsheetUrl to blank.measureName to blank.Set measureName to blank.recipientEmail to blank.Set recipientEmail to blank.historyTab to History.Set historyTab to History.maxPoints to 24.Set maxPoints to 24.measureName and save it as measureLabel.Trim measureName and save it as measureLabel.measureLabel lowercase and save it as wantedMeasure.Make measureLabel lowercase and save it as wantedMeasure.spreadsheetUrl. Save it as history.Read the rows of spreadsheetUrl. Save it as history.periodLabels to an empty list.Set periodLabels to an empty list.pointValues to an empty list.Set pointValues to an empty list.readingLines to an empty list.Set readingLines to an empty list.leftOff to an empty list.Set leftOff to an empty list.entry in history.rows:For each entry in history.rows:rowErrorIf a step below fails, save the error as rowErrorentry.Measure lowercase and save it as rowMeasure.Make entry.Measure lowercase and save it as rowMeasure.rowMeasure is wantedMeasureIf rowMeasure is wantedMeasureentry.Period and save it as periodRaw.Trim entry.Period and save it as periodRaw.periodRaw with "Trim periodRaw with
replaced by with , replaced by with replaced by and save it as period.entry.Value and save it as reading.Extract the first number from entry.Value and save it as reading.period is emptyIf period is emptya 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.period to the end of leftOff and save it as leftOff.Add period to the end of leftOff and save it as leftOff.period to the end of periodLabels and save it as periodLabels.Add period to the end of periodLabels and save it as periodLabels.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.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.rowError as text.Show in the output: Skipped a row that could not be read: rowError as text.cappedNote to blank.Set cappedNote to blank.takeFromEnd to 0 minus maxPoints.Set takeFromEnd to 0 minus maxPoints.periodLabels is greater than maxPointsIf the length of periodLabels is greater than maxPointsperiodLabels from position takeFromEnd on and save it as periodLabels.Take periodLabels from position takeFromEnd on and save it as periodLabels.pointValues from position takeFromEnd on and save it as pointValues.Take pointValues from position takeFromEnd on and save it as pointValues.readingLines from position takeFromEnd on and save it as readingLines.Take readingLines from position takeFromEnd on and save it as readingLines.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.periodLabels is emptyIf periodLabels is emptyrecipientEmail.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.periodLabels with , and save it as labelsText.Join periodLabels with , and save it as labelsText.pointValues with , and save it as valuesText.Join pointValues with , and save it as valuesText.readingLines with a new line and save it as numbersText.Join readingLines with a new line and save it as numbersText.chartUrl to blank.Set chartUrl to blank.chartErrorIf a step below fails, save the error as chartErrormeasureLabel. Save it as chart.Create a line chart titled measureLabel. Save it as chart.chart.url and save it as chartUrl.Trim chart.url and save it as chartUrl.chartError as text.Show in the output: The chart could not be drawn: chartError as text.note to blank.Set note to blank.noteErrorIf a step below fails, save the error as noteErrormeasureLabel
Readings:
numbersText'. Save it as written.
Maximum response length: 400written.response_text and save it as note.Trim written.response_text and save it as note.noteError as text.Show in the output: The note on what changed could not be written: noteError as text.bodyParts to an empty list.Set bodyParts to an empty list.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.note isn't emptyIf note isn't emptynote to the end of bodyParts and save it as bodyParts.chartUrl is emptyIf chartUrl is emptyThe 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.chartUrl to the end of bodyParts and save it as bodyParts.numbersText to the end of bodyParts and save it as bodyParts.cappedNote isn't emptyIf cappedNote isn't emptycappedNote to the end of bodyParts and save it as bodyParts.Add cappedNote to the end of bodyParts and save it as bodyParts.leftOff isn't emptyIf leftOff isn't emptyleftOff 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.bodyParts with a blank line and save it as emailBody.Join bodyParts with a blank line and save it as emailBody.recipientEmail.Send an email to recipientEmail.
Subject: measureLabel - how it has movedemailBody