Filename of imported json file

In relation to this thread: State tiddler dependent on tiddler title, I would like to capture the filename of the imported json file and use that in my wiki for reference. This way I will be able to see what the last json file I imported was.

The below code is what I use to import the json file. It will delete all current logs, stations, etc. and import a new log json file. I then create the station, date and other tiddlers with another button.

Is there a way to be able to store the filename in a field of my “Refresh Logs” tiddler?

\define dx-refresh-logs()
<div class="tc-block">

  <!-- Button: clear existing DXLog tiddlers -->
  <$button class="tc-btn-invisible tc-btn-large">
    <$list filter="[[$:/core/images/refresh-button]is[tiddler]]">
      <$transclude tiddler="$:/core/images/refresh-button" />
    </$list>
    <span>Clear Current Logs</span>

    <!-- Delete all tiddlers tagged DXLog / DXStation -->
    <$action-deletetiddler $filter="[tag[DXLog]]" />
    <$action-deletetiddler $filter="[tag[DXStation]]" />
    <$action-deletetiddler $filter="[tag[DXLogDate]]" />

    <!-- NEW: record last refresh time + file on DX Refresh Logs -->
<$action-setfield
  $tiddler="Refresh Logs"
  last-refresh=<<now "YYYY-0MM-0DD 0hh:0mm:ss">>
/>
  </$button>

</div>

<div class="tc-block" style="margin-top:0.5em;">

  <!-- Browse control: choose the new JSON export -->
  <span class="tc-file-input-wrapper browse-button">
    <$browse accept=".json" tooltip="Import DX JSON file"/>
    <span>Re-import JSON</span>
  </span>

</div>
\end

\define dx-create-station-tiddlers()
<!-- Only look at imported DX log tiddlers -->
  <$list
    filter="[tag[DXLog]has[dx-station]get[dx-station]trim[]unique[]]"
    variable="station"
  >
    <$let
      logs={{{ [tag[DXLog]field:dx-station<station>] }}}
      class={{{ [<logs>get[dx-class]trim[]unique[]join[, ]] }}}
      loc={{{ [<logs>get[dx-tx-location]trim[]unique[]join[, ]] }}}
      state={{{ [<logs>get[dx-state]trim[]unique[]join[, ]] }}}
      country={{{ [<logs>get[dx-country]trim[]unique[]join[, ]] }}}
      frequency={{{ [<logs>get[dx-freq-khz]trim[]unique[]join[, ]] }}}
      count={{{ [tag[DXLog]field:dx-station<station>count[]] }}}
    >
      <$action-createtiddler
        $basetitle=<<station>>
        $overwrite="yes"
        text=""
        tags="DXStation"
        station-class=<<cls>>
        station-tx-location=<<loc>>
        station-state=<<state>>
        station-country=<<country>>
        station-class=<<class>>
        station-freq-khz=<<frequency>>
        station-count=<<count>>
      />
    </$let>
  </$list>
\end

\define dx-create-date-tiddlers()
  <$list filter="[tag[DXLog]sort[dx-date]reverse[yes]get[dx-date]unique[]]" variable="dxlogdate">
    <$let
      dxdate=<<dxlogdate>>
      dxdatecount={{{ [tag[DXLog]dx-date<dxlogdate>count[]] }}}
      dxnewcount={{{ [tag[DXLog]dx-date<dxlogdate>dx-new-station[Y]count[]] }}}
      dxlogqth={{{ [tag[DXLog]dx-date<dxlogdate>get[dx-qth]] }}}
    >
      <$action-createtiddler
        $basetitle=<<dxdate>>
        $overwrite="yes"
        text=""
        tags="DXLogDate"
        logs=<<dxdatecount>>
        new=<<dxnewcount>>
        qth=<<dxlogqth>>
      />
    </$let>
  </$list>
\end

\define dx-create-qth-tiddlers()
  <$list filter="[tag[DXLog]sort[dx-date]reverse[yes]get[dx-qth]unique[]]" variable="dxlogqth">
    <$let
      dxqth=<<dxlogqth>>
      dxqthlogcount={{{ [tag[DXLog]dx-qth<dxlogqth>count[]] }}}
      dxqthnewcount={{{ [tag[DXLog]dx-qth<dxlogqth>dx-new-station[Y]count[]] }}}
    >
      <$action-createtiddler
        $basetitle=<<dxqth>>
        $overwrite="yes"
        text=""
        tags="DXQTH"
        logs=<<dxqthlogcount>>
        new=<<dxqthnewcount>>
      />
    </$let>
  </$list>
\end

\define dxactions()
  <<dx-create-station-tiddlers>>
  <<dx-create-date-tiddlers>>
  <<dx-create-qth-tiddlers>>
\end

<table class="table-borderless">
  <tr>
    <td style="padding: 0;"><b>Logs last updated:</b></td>
    <td>{{Refresh Logs!!last-refresh}}</td>
  </tr>
  <tr>
    <td style="padding: 0;"><b>Log file used:</b></td>
    <td>{{Refresh Logs!!last-file}}</td>
  </tr>
</table>


<<dx-refresh-logs>>

<$button class="tc-btn-invisible" actions=<<dxactions>>>
Create / update station tiddlers
</$button>

Without looking at the detail of the above solution, when importing the filename may be accessible but of course not the full path to the filename. This is obscured so websites cant harvest information about the local machine. File names are often used to determine the file type and then apply a mime type or formatting, so filenames can be available.

Another way to handle this is to get your wiki to prompt for the full path and filename and save that in your wiki, you could do this and then generate a button to import. Similarly you could add these details to an imported/exported file / tiddler, after.

On windows I have found it is also possible when importing a single file, in the file open dialogue, use a r-click copy as path, before hitting OK to import. Or similarly after exporting a file, go to “export a second time”, the folder you saved the file in will be current (Depending on browser settings) and there you can r-click copy as path, and cancel the second export. In both cases the next step is to “paste” the path and filename into you wiki, such as into a tiddler field.

  • I have made buttons that on click copy this full path to the clipboard, so when the save as, or open dialogue pops up I can use ctrl-V and hit ok to save/open.
  • Also using the $browse widget I can set the filetypes to *.json to I only see *.json files, this helps with searching as well by limiting searches to JSON and ignoring wikis and wiki backups.

I have crafted my own package management tools and include fields in the “root tiddler” within a JSON package that have this full path and filename stored for reference. Unless its moved I need only do this once for each JSON.

Since there are limits on general browsers consider using TiddlyDesktop or one of its flavours for more local file access.

This is a great idea! I too would like for the filename (not path) to be accessible.

Your idea focuses on being able to “see” what the last import filename was.

For me, I’d love to have it available to become a field-value (say, through import actions, or Commander post-import batch processing) for the imported batch. Similar reasons, plus a bit more long-term tracking in case that helps with troubleshooting.

(Background: I end up with batch imports of datasets — whose json structure is not controlled by me — that get bigger and bigger over the course of several weeks, and for various reasons uniqueness of “title” sometimes fails. So I do often err on the side of importing duplicates, and then need easy ways to peer into which ones should be preserved.)

My bundler plugin contains an Import Bundle Setting, which is active by default.

To experiment with it, you can drag and drop import a JSON file to the edition. You can test what it does. May be the info contained in the import.bundle tiddler can help you.

I did not analyse your code.
-m

The following is not a solution to your question, but is instead some pointers on simplifying your wikitext code.

When transcluding a TWCore image, you don’t need to check if it exists, and you can use the {{...}} short-form instead of the $transclude widget.

Change

<$list filter="[[$:/core/images/refresh-button]is[tiddler]]">
   <$transclude tiddler="$:/core/images/refresh-button" />
</$list>

to

{{$:/core/images/refresh-button}}

$action-deletetiddler with the $filter param can combine multiple filter runs.

Change

<$action-deletetiddler $filter="[tag[DXLog]]" />
<$action-deletetiddler $filter="[tag[DXStation]]" />
<$action-deletetiddler $filter="[tag[DXLogDate]]" />

to

<$action-deletetiddler $filter="[tag[DXLog]] [tag[DXStation]] [tag[DXLogDate]]" />

has[fieldname] checks for fields with non-blank content, but get[fieldname] automatically excludes fields with blank content so using has[...] before get[...] is not needed.

Change

filter="[tag[DXLog]has[dx-station]get[dx-station]trim[]unique[]]"

to

filter="[tag[DXLog]get[dx-station]trim[]unique[]]"

The reverse[] filter operator does not use a parameter value. Also, the sort[] filter operator defaults to sorting the current list item values in ascending order and accepts “!” as a prefix to apply descending order, so reverse[] is not needed. In addition, it is also generally more efficient to leave the sort processing to the end of the filter (when there may be less items to sort). Thus, you can replace:

filter="[tag[DXLog]sort[dx-date]reverse[yes]get[dx-date]unique[]]"

to

filter="[tag[DXLog]get[dx-date]unique[]!sort[]]"

Note that sort[] does not need to specify the field name, since it is sorting the values that are the result of the get[dx-date] rather than the titles of tiddlers that have dx-date fields.

-e

2 Likes

@Springer, having the filename in a field of the tiddler where my code resides is my goal. I can then reference that field to display the filename on the home page of my wiki as last file imported.

@pmario, thanks for recommending the Bundler plugin. Unfortunately, it does not fill my needs presently. I like the idea of a tiddler representing the last import, but it does not contain the filename of the file I imported. Is there a way that could be accomplished?

@EricShulman, thanks for the coding tips. I will certainly take advantage of those. Any thoughts on how to access the filename? I don’t need the full path, just the filename so it can be stored in a field.

All,

Just for grins, I asked ChatGPT for help with the issue of storing the filename of the file imported. It suggesting using the javascript code below in a startup tiddler. It actually worked! The filename was stored in a field of the tiddler I use to trigger the import. Before I started using it for anything real, I wanted to get everyone’s opinion on the code. I know very little about javascript. Is this actually a good solution?

Thanks again in advance!

/*\
title: $:/startup/DXLogLastFile.js
type: application/javascript
module-type: startup
\*/
(function () {
  "use strict";

  exports.name = "DXLogLastFile";
  exports.platforms = ["browser"];
  exports.after = ["startup"];

  exports.startup = function () {
    console.log("DXLogLastFile: startup loaded (catch-all)");

    document.addEventListener(
      "change",
      function (event) {
        var el = event.target;

        // Catch ANY file input, regardless of how <$browse> wraps it
        if (!el || el.tagName !== "INPUT" || el.type !== "file") return;
        if (!el.files || !el.files.length) return;

        var f = el.files[0];
        var filename = (f && f.name) ? String(f.name) : "";
        if (!filename) return;

        // Only act on JSON files
        if (!/\.json$/i.test(filename)) {
          console.log("DXLogLastFile: ignored non-json", filename);
          return;
        }

        // Create/update fields (setText creates the tiddler if it doesn't exist)
        $tw.wiki.setText("Refresh Logs", "last-file", undefined, filename);

        var ts = $tw.utils.formatDateString(new Date(), "YYYY-0MM-0DD 0hh:0mm:ss");
        $tw.wiki.setText("Refresh Logs", "last-refresh", undefined, ts);

        console.log("DXLogLastFile: stored", filename);
      },
      true
    );
  };
})();

I can’t review JavaScript much but to let you know that I created what I call package tiddlers that contain a filter of tiddlers to include when exported. I then copy the path and file name and save that in a field of the package tiddlers, and save again.

this has been enough for me to manage a large library of packages, along with some additional features

Presumably that makes a difference only for packages / json bundles that you create (or edit), right?

As someone whose workflow involves frequently importing from json resources not created or controlled by me, I’m eager to try the solution drafted in the post by @HistoryBuff — though it’s not yet urgent enough to be on the front-burner, and I don’t know javascript well enough to feel confident with just trying it in haste.

@Springer - that’s exactly why I posted this post. I really don’t know javascript at all so wanted some input from folks that do. I did try it in a copy of my wiki and it worked as intended. However, I’m just wanted some feedback before I went “production” with it.