How do you handle/log historic app view record data?

Hi all,
I am just curious about this. I am thinking about building a dashboard app to show an static “photo” of the state of some app view that shows some filtered fields in a given moment.

Do you do this? How do you handle it?

For example, imagine a simple app with a price record field. I want to “log” the price of the existing records in the view each 1st day of the month. Then I would love to visualize it. I don’t need a dashboard necessarily, a table could make it, although some visuals would maybe add more to it, but I was trying to think about a solution that is not involving a dedicated app or more complexity (the simpler the better here).

Also, as a side question, is there a way to retrieve exactly what records composed a view, so how this view was in x day (of the past) and with their exact field values? This exact values mean that some values could have changed so that the record isn’t included anymore in the view, and also think about some other field changes like price.

Thanks a lot.
Best,
R.J.

Hi @R.J
Your first question I am not sure I have an answer you want :wink: however: I would probably create a different app (which you said you didn’t want to do) or pass the data to an external DB but two other possibilities could be:
1. Create 12 hidden (this I think will easier with the new record) number fields on the records then at the end of each month write the value into the relevant months field then you can ‘dashboard’ on the fields.

  1. Create a hidden text field then at the end of the month add the months value in this way:

    [
      ["Month", "Value"],
      ["Jan", 2345],
      ["Feb", 2340]
    ]
    

You could then use a little script that pushed the current months value into the main array. From here you add a calculation field to the record and build a table from the JSON in the Text field.

let raw = ï»ż@dataï»ż;

// Try to parse, fallback to default if it fails - You probably don't need the first try block but when I was building it was messing me about
let data;
try {
 data = JSON.parse(raw);
} catch (e) {
 data = [["Month", "Value"]];
}
// main table build
let table = "<table border='1' style='border-collapse: collapse;'>";
for (let i = 0; i < data.length; i++) {
 table += "<tr>";
 for (let j = 0; j < data[i].length; j++) {
  let cellTag = i === 0 ? "th" : "td";
  table += `<${cellTag} style='padding: 4px 8px;'>${data[i][j]}</${cellTag}>`;
 }
 table += "</tr>";
}
table += "</table>";
//Display the table
table;

When I was building the table it was messing me around a little so I added the try at the top if you don’t want it and you are sure you have suitable data in your text field you could remove it and replace the first line with:

let data = JSON.parse(@data)

NOTE: I have used a single line text field in my demo as it will leave your JSON intact however this has a length of 500 characters so you need to be carefull how much you put in.

Part Two

Not easily is as far as I am aware I think i would do it in one of two ways:
1. track changes by pushing changes to an external DB then doing the queries there

  1. It is technically possible to do this in Tape but it might depend on how many records you are trying to do this for, the theoretical process would be:
    1. Search your records for those changed since your cut of date
    2. Loop through those changed getting the revision history
tape.setApiKey(var_api);
const { data: rev } = await tape.RecordRevision.getMany(current_record_id);
console.info(JSON.stringify(rev,null,2));

When you get the revision history you get a response like:

{
  "revisions": [
    {
      "created_by": {
        "id": 67022,
        "mail": [
          "jason@jmc.tools"
        ],
        "user_id": 67022,
        "name": "Jason",
        "org_id": 1703,
        "type": "user"
      },
      "created_on": "2025-05-09 07:44:52",
      "record_id": 153768387,
      "revision": 0,
      "type": "creation"
    },
    {
      "created_by": null,
      "created_on": "2025-05-09 07:44:56",
      "record_id": 153768387,
      "revision": 1,
      "type": "update"
    },
    {
      "created_by": {
        "id": 67022,
        "mail": [
          "jason@jmc.tools"
        ],
        "user_id": 67022,
        "name": "Jason",
        "org_id": 1703,
        "type": "user"
      },
      "created_on": "2025-05-09 07:57:52",
      "record_id": 153768387,
      "revision": 2,
      "type": "update"
    }
  ],
  "total": 3
}

From this you would need to:

  1. extract the dates
  2. find the date closest to your cut off date but more recent
  3. get the revision number
  4. subtract 1 from that number

This if my brain is working correctly will give you the record revision for the state at your cut off date, so armed with that number you can now get the record at that revision

const { data: oldRecord } = await tape.RecordRevision.getRevision({recordId: current_record_id,revisionId: 1});
console.info(JSON.stringify(oldRecord,null,2));

which will give you the record details for that revision:

{
  "record_id": 153768387,
  "app_record_id": 1,
  "app_id": 60518,
  "created_on": "2025-05-09 08:44:52",
  "created_on_utc": "2025-05-09 07:44:52",
  "last_modified_on": "2025-05-09 08:57:52",
  "last_modified_on_utc": "2025-05-09 07:57:52",
  "deleted_on": null,
  "deleted_on_utc": null,
  "fields": [
    {
      "field_id": 610930,
      "external_id": "name",
      "label": "Name",
      "type": "text",
      "field_type": "single_text",
      "config": {
        "label": "Name",
        "slug": "name",
        "external_id": "name",
        "show_description": false,
        "required": false,
        "always_hidden": false,
        "hidden_if_empty": false,
        "settings": {
          "formatted": false
        }
      },
      "values": [
        {
          "value": "t1"
        }
      ]
    },
    {
      "field_id": 610932,
      "external_id": "data",
      "label": "data",
      "type": "text",
      "field_type": "single_text",
      "config": {
        "label": "data",
        "slug": "data",
        "external_id": "data",
        "show_description": false,
        "required": false,
        "always_hidden": false,
        "hidden_if_empty": false,
        "settings": {
          "formatted": false
        }
      },
      "values": [
        {
          "value": "[[\"Month\", \"Value\"], [\"Jan\", 2345], [\"Feb\", 2340]]"
        }
      ]
    },
    {
      "field_id": 610933,
      "external_id": "calculation",
      "label": "Calculation",
      "type": "calculation",
      "field_type": "calculation",
      "config": {
        "label": "Calculation",
        "slug": "calculation",
        "external_id": "calculation",
        "show_description": false,
        "required": false,
        "always_hidden": false,
        "hidden_if_empty": false,
        "settings": {
          "decimals": 0,
          "return_type": "text",
          "script": "let raw = @[data](field_610932);\n\n// Try to parse, fallback to default if it fails\nlet data;\ntry {\n data = JSON.parse(raw);\n} catch (e) {\n data = [[\"Month\", \"Value\"]];\n}\n\nlet table = \"<table border='1' style='border-collapse: collapse;'>\";\n\nfor (let i = 0; i < data.length; i++) {\n table += \"<tr>\";\n for (let j = 0; j < data[i].length; j++) {\n  let cellTag = i === 0 ? \"th\" : \"td\";\n  table += `<${cellTag} style='padding: 4px 8px;'>${data[i][j]}</${cellTag}>`;\n }\n table += \"</tr>\";\n}\n\ntable += \"</table>\";\n\ntable;",
          "calendar": false,
          "unit": null
        }
      },
      "values": [
        {
          "value": "<table border='1' style='border-collapse: collapse;'><tr><th style='padding: 4px 8px;'>Month</th><th style='padding: 4px 8px;'>Value</th></tr><tr><td style='padding: 4px 8px;'>Jan</td><td style='padding: 4px 8px;'>2345</td></tr><tr><td style='padding: 4px 8px;'>Feb</td><td style='padding: 4px 8px;'>2340</td></tr></table>",
          "value_string": "<table border='1' style='border-collapse: collapse;'><tr><th style='padding: 4px 8px;'>Month</th><th style='padding: 4px 8px;'>Value</th></tr><tr><td style='padding: 4px 8px;'>Jan</td><td style='padding: 4px 8px;'>2345</td></tr><tr><td style='padding: 4px 8px;'>Feb</td><td style='padding: 4px 8px;'>2340</td></tr></table>",
          "decimal": null,
          "start": null,
          "start_date": null,
          "start_date_utc": null,
          "start_time": null,
          "start_time_utc": null,
          "start_utc": null
        }
      ]
    }
  ],
  "created_by": {
    "mail": [
      "jason@jmc.tools"
    ],
    "id": 67022,
    "user_id": 67022,
    "org_id": 1703,
    "name": "Jason",
    "type": "user",
    "email": "jason@jmc.tools"
  },
  "last_modified_by": {
    "mail": [
      "jason@jmc.tools"
    ],
    "id": 67022,
    "user_id": 67022,
    "org_id": 1703,
    "name": "Jason",
    "type": "user",
    "email": "jason@jmc.tools"
  }
}

Now you will need to check the record to see if it would have fallen into your view and if it would save the record_id and the revision id for doing whatever you want with later.

I don’t think any of that actually gives you what you were after but hopefully it gives you some ideas. However bottom line is if you want ‘point in time’ data I would pass it to something else: a different app, external SQL or even the JSON service that @Luis talked about in the April 2025 Official Tape Partner Roundtable Webinar.

3 Likes

Hi Jason,
Thank you very much for your extensive and crafted reply. This thread was intended as a reflective discussion on these topics, so I appreciate your deep thoughts here.


First question
So for the first question, I feel you :sweat_smile: , and although I didn’t want to maybe the extra app makes the most sense or is the easiest while adding complexity (and messing search a bit). The app I was thinking while writing is one with lots of fields and almost with all types on them (except for the files I think). So I was trying to avoid more fields as well.
To add to this first question, does someone know if there is a simple way to log dashboard reports results in a given moment? How do/would you handle this?
This dashboard ones could be solved with some automations that repeat and “emulate” the dashboard report calculations and then logging these in a dedicated app. But this adds complexity, load and more work to create all these (especially if there are a lot). I am just curious and thought that there could be another better way to access this.
Are there dashboard automations planned? Making these kind of report values accessible in the automations could ease things up a lot. Maybe this is actually possible and I missed it (thus asking). :joy:


Second question
For the second part, as you mentioned, this is not exactly what I was after but nonetheless very very insightful. I can more or less see how a workaround could be built using your reasoning and examples. And definitely I see the value here of the service Luis talked about.
It would be very nice and useful to have a simpler way to access this information, whether using some kind of “time filter” in the app view modal or some simple access in the automations (or some other way the simpler the better).


Example
Let me use a quick example to better see all this. Imagine a stocks app (the original app wasn’t but this could easily illustrate everything).

You could have many fields with lots of info and different field types (categories for industries, prices, metrics, data, text
).

We could make some views to see the ones between a price bracket lets say $100-$200 in a given index.

Let’s add some reports in a dashboard showing some reports showing ratios related to the views.

As you know, these stock prices are going to change over the time so the records in the view may change. Some may actually be excluded from the view. This would alter these dashboard reports as well.

I hope this is easier now to see the questions. Just to add a bit more, you could need or want in the future to calculate new dashboard report ratios from a view. Let’s say you want to analyze the value of this freshly created ratio but for the records that composed the view a couple of months ago (and with the record field values of a couple of months ago, not the current price for example).

Thanks a lot for reading all this and for taking the time to think about this.
Best,
R.J.