Posted

in

JSON Schemas and Making a Versatile Application.

JSON Schemas and Making a Versatile Application.

Please excuse the big wad of JSON in the middle of the post.

Things have come a long way from the previous post (diehealthy.org: 27 October 2022: “Working on a Local Single Page Application.”) about working on a local file that provides a whole data tracking. While I originally wanted it to be used for games, I’d seen other JSON-schema-based stuff online and decided it could be fully schema driven. While it doesn’t yet edit its own schemas, that’s probably feasible in the long run (if not something I’ll necessarily do).

In order to further refine it toward that schema system, I forked it and made a version to track the movies I’ve watch. The post image is of that version, though they are currently at parity aside from the schema differences.

To give an idea what I mean by schema, here’s some excerpts from the film version:

{
    "name": "filmtracker",
    "defaults": {
        "filters": {
            "recorded": 1,
            "watched": 0
        }
    },
    "props": [
        {
            "name": "name",
            "title": "Name",
            "type": "string",
            "display": "headertext",
            "displaystyle": "italics",
            "sortOn": true,
            "req": true,
            "def": null
        },
        {
            "name": "recorddata",
            "title": "Record data",
            "type": "group",
            "props": [
                {
                    "name": "added",
                    "title": "Added",
                    "hidden": true,
                    "display": "fulldate",
                    "displayprefix": "Added on",
                    "type": "date",
                    "sortOn": true
                },
                {
                    "name": "modified",
                    "title": "Modified",
                    "hidden": true,
                    "display": "fulldate",
                    "displayprefix": "Updated on",
                    "type": "date",
                    "sortOn": true
                }
            ]
        },
        {
            "name": "watchdata",
            "title": "Watch info",
            "type": "group",
            "props": [
                {
                    "name": "recorded",
                    "title": "Recorded",
                    "type": "bool",
                    "display": "iffalse",
                    "displayvalue": "Unrecorded.",
                    "summaryvalue": "📅",
                    "filter": true,
                    "def": 0
                },
                {
                    "name": "watched",
                    "title": "Watched",
                    "type": "bool",
                    "display": "text",
                    "displaydepends": [
                        "recorded"
                    ],
                    "displayvalues": [
                        "Unseen",
                        "Seen"
                    ],
                    "summaryvalues": [
                        "💾",
                        "🍿"
                    ],
                    "filter": true,
                    "def": 0
                },
                {
                    "name": "watchdate",
                    "title": "Date of watch",
                    "type": "date",
                    "display": "monthdate",
                    "displaydepends": [
                        "watched"
                    ],
                    "displayprefix": "Watched",
                    "sortOn": true,
                    "sortDep": "bought",
                    "def": ""
                }
            ]
        },
        {
            "name": "filmdata",
            "title": "Film info",
            "type": "group",
            "props": [
                {
                    "name": "releaseyear",
                    "title": "Year of Release",
                    "type": "num",
                    "display": "text",
                    "displayprefix": "Year:",
                    "sortOn": true,
                    "minv": 1900,
                    "def": 2022
                },
                {
                    "name": "score",
                    "title": "Review score",
                    "type": "range",
                    "display": "stars",
                    "displaydepends": [
                        "watched"
                    ],
                    "displayprefix": "Score:",
                    "summaryvalue": "stars",
                    "sortOn": true,
                    "sortDep": "watched",
                    "minv": 0,
                    "maxv": 5,
                    "step": 1,
                    "def": 0
                }
            ]
        },
        {
            "name": "categories",
            "title": "Tags",
            "type": "strarray",
            "display": "ulist",
            "def": null
        },
        {
            "name": "notes",
            "title": "Notes",
            "type": "text",
            "display": "longtext",
            "summary": "📓",
            "def": null
        }
    ]
}

It’s quite a lot, and messy (as is the rest of the application so far), and it mixes data definitions with their presentation, but it’s mostly serviceable. But it lets me define, with the current version:

  • groups
  • numeric inputs
  • string inputs
  • url inputs (unvalidated at the moment)
  • date inputs (though I’d prefer to move to month/year or such)
  • boolean inputs (implemented as radio buttons)
  • tri inputs (radio buttons)
  • range inputs (radio buttons)
  • text inputs (text boxes)
  • string array inputs (for tags, with autocomplete!)

I’d originally gone with checkboxes and three-state checkboxes for booleans and tris, but decided it was too much trouble and having separate buttons looked nicer. The basic checkboxes were reused instead for toggling off or on the listing filters for boolean and tri fields.

It can also search in and sort the listing.


One of the nicer parts of all this is the ability to export and import from JSON, as well as being able to use the browser console to loop through and batch-modify the data. I’m not sure how feasible it would be to implement that through UI, which would be more useful to nontechnical users. But for now this is just a little project to handle my own data.

If I can get it cleaned up, I’ll probably throw a copy up here at some point, though so far I’m still iterating on it a good bit as I go through my old list of movies I watched and fill in their details. I started keeping a list at some point last year, and that was title-only, so it’s been a lot of searching, figuring out which was the movie I actually watched, trying to remember enough to give it a rating.

But the main goal is that if I decide I need a list of something, I can copy the file over, throw a schema together, and start making the list in a way that lets me update it or review it better than a text file and not requiring the ugliness of a spreadsheet. Like when I kept a list of the legislators who objected on 6 January 2021, that’s just a text file. Next time it could be a nice little application that makes the task not necessarily easier, but more useful:

  1. Get a text list of the things
  2. Some light massaging to turn it into JSON
  3. Write a quick schema
  4. Import the data
  5. Fill in any details, be able to filter, sort, search easily.

I can only imagine what data wrangling looks like in 100 years. I hope by then it’s all AI user interfaces that look pretty are functional no friction. Until then, this project has reminded me how powerful, how versatile, and above all how fairly simple, HTML plus Javascript has gotten.


Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.