History tracking "change_log" field using trigger formulas and widgets

Hello ^^

There are some tables where values change over time, and when those changes occurred is significant. For example a todo list, or a kanban board. I know grist has a perfectly good backup and undo/history true, but as far as I could tell, this couldn’t be seen easily at a per-record level, and couldn’t easily be incorporated into, say a custom widget.

I built it using trigger formulas (on new and update records). This feels like a very hacky solution. It would be cool feature if you could turn on history tracking for a record natively in Grist. But for now, this might work :smiley: If you know a better way to do this, please let me know!

The code and a readme are here: GitHub - kindrowboat/grist-change_log: a (probably cursed) way to have changelogs for records in grist

This is what the trigger formula currently looks like:

# ====================8<-----------------------------------
blacklist = ["change_log", "updated_at", "created_at"]
# ----------------------------->8==========================

import json

def jsonify_rec(changed_record):
  def simplify(value):
    if ISREF(value):
      return value.id
    elif ISREFLIST(value):
      return ["L"] + [v.id for v in value]
    elif isinstance(value, tuple):
        return list(value)
    return value

  result = {k: simplify(v) for k, v in changed_record.items() if k not in blacklist}
  return result

# Trigger formula code
current_snapshot = jsonify_rec(RECORD(rec, dates_as_iso=True))
logs_json = current_snapshot.get('change_log') or "[]"
logs = json.loads(logs_json)
previous_snapshot = logs[0]["snapshot"] if logs else {}

changed_fields = [
  field for field in current_snapshot
  if current_snapshot.get(field) != previous_snapshot.get(field)
]

log = {
  "timestamp": NOW().isoformat(),
  "changed": changed_fields,
  "snapshot": current_snapshot
}
logs.insert(0, log)  # Prepend the new log entry
return json.dumps(logs)

There’s also a custom widget you can install to view a concise change log for a given record.

5 Likes

Thanks for this! I’m new to grist, so I don’t fully understand how everything works yet. It didn’t work for me at first, it would always overwrite the preview entries, so I changed the line logs_json = value or "[]", and that fixed it for me so that it now keeps history. I believe this is because current_snapshot will never have a change_log entry, since it is being filtered out via the blacklist. Once that was changed, the UI component worked great. I’ll probably also tweak the reference list handling, since it is currently showing “L” for empty reference lists.

1 Like

Thanks! The most up-to-date version of the history tracking trigger formula is at grist-change_log/trigger_function.py at main · kindrowboat/grist-change_log · GitHub

If you make any improvement, please feel free to open a pull request <3

1 Like

Very cool! I had the same issue as @Peter_Steffey but his tweak worked as well. Thanks for sharing.