Recent chases list: Make callsign clickable

Hi everyone,

I don’t know if I’m asking for too much, but I would love to see a very simple feature implemented: It would be great if I could click on a callsign in the “Other Callsign” column in the recent chases list and then see the log of that particular activation.

I often try to check if people got my callsign right. I’m new to CW, and the DA* prefix is also a rather new one here in Germany, so sometimes people log me as DL2PK. I also find it interesting to see how many contacts other activators made and which stations they worked.

Would this be a feature you would consider adding?

73 de DA2PK

1 Like

Hi Philipp,

You can check that on Sotl.as, find the call and the activation, click on and check if your call is correct on the log :wink:

73, Éric F5JKK

1 Like

In the interim, you can view who chased you from the sotadata site:

Find your log and click on the eye for the one you want:

Click the show who chased me button:

Compare the two tables, with the activator callsign showing what the other station logged:

I think Philipp asked like a chaser

1 Like

I wasn’t sure, but with sotlas for others’ activations and sotadata for his activations then everything is covered at the moment!

Screenshots for completeness:

Find the activator and click on the summit log of interest:

Voilà:

1 Like

Sorry, yes; I should have clarified: I’m asking about the chaser logs on the SOTAdata website.

For now, I always have to copy and paste https://www.sotadata.org.uk/en/stats/ with the callsign appended into my browser’s address bar.

sot.las is - as always :slight_smile: - excellent in this regard but I’m specifically asking for sotadata.

1 Like

I have created a userscript for Firefox that implements clickable callsigns. I’m using it together with the Violentmonkey browser extension. Sharing it here, hoping that others might find it useful as well.

// ==UserScript==
// @name         SOTAdata – Clickable Callsigns
// @namespace    https://syslinx.org/
// @version      0.1
// @description  Makes callsigns clickable and adds activator log link column
// @author       DA2PK
// @match        https://www.sotadata.org.uk/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  const BASE_CALL = '[A-Z0-9]{1,3}[0-9][A-Z]{1,4}';
  const CALLSIGN_RE = new RegExp(`^([A-Z0-9]{1,3}\\/)?${BASE_CALL}(\\/[A-Z0-9]{1,4})?$`);
  const FREQ_RE = /^[0-9]/;
  const STATS_BASE = 'https://www.sotadata.org.uk/en/stats/';
  const LOGS_BASE  = 'https://www.sotadata.org.uk/en/logs/activator/';

  // CT7/DA2PK/P → DA2PK, DA2PK/P → DA2PK, DA2PK → DA2PK
  function extractBaseCallsign(text) {
    const parts = text.split('/');
    const base = parts.find(p => /[0-9]/.test(p) && p.length > 2);
    return base || text;
  }

  function processRow(row) {
    if (row.querySelector('td.sota-log-cell')) return;
    const tds = row.querySelectorAll('td');
    if (tds.length !== 7) return;

    const callsignTd = tds[2];
    const rawText = callsignTd.textContent.trim();
    const text = rawText.toUpperCase();

    if (!CALLSIGN_RE.test(text) || FREQ_RE.test(text)) return;
    if (callsignTd.querySelector('a')) return;

    const baseCallsign = extractBaseCallsign(text);

    // Callsign → Stats-Link
    const statsLink = document.createElement('a');
    statsLink.href = STATS_BASE + encodeURIComponent(baseCallsign);
    statsLink.textContent = rawText;
    statsLink.target = '_blank';
    statsLink.rel = 'noopener noreferrer';
    statsLink.title = `SOTAdata Stats für ${baseCallsign}`;
    statsLink.style.cssText = 'color: inherit; text-decoration: underline dotted; cursor: pointer;';
    callsignTd.textContent = '';
    callsignTd.appendChild(statsLink);

    // Neue Spalte: Log-Link
    const newTd = document.createElement('td');
    newTd.className = 'sota-log-cell';
    newTd.style.textAlign = 'center';

    const logLink = document.createElement('a');
    logLink.href = LOGS_BASE + encodeURIComponent(baseCallsign);
    logLink.target = '_blank';
    logLink.rel = 'noopener noreferrer';
    logLink.title = `Aktivierungs-Logs von ${baseCallsign}`;
    logLink.style.cssText = 'text-decoration: none; border: none;';
    const emoji = document.createElement('span');
    emoji.textContent = '📋';
    logLink.appendChild(emoji);
    newTd.appendChild(logLink);

    const mapTd = tds[tds.length - 1];
    row.insertBefore(newTd, mapTd);
  }

  function patchHeader() {
    document.querySelectorAll('thead').forEach(thead => {
      const ths = thead.querySelectorAll('th');
      if (ths.length < 7) return;
      if (thead.dataset.sotaHeaderPatched) return;
      const lastTh = ths[ths.length - 1];
      if (lastTh.textContent.trim() !== 'Map') return;
      thead.dataset.sotaHeaderPatched = '1';
      const newTh = document.createElement('th');
      newTh.textContent = 'Log';
      newTh.style.textAlign = 'center';
      thead.insertBefore(newTh, lastTh);
    });
  }

  const observer = new MutationObserver(() => {
    patchHeader();
    document.querySelectorAll('tr').forEach(processRow);
  });

  observer.observe(document.body, { childList: true, subtree: true });
  patchHeader();
  document.querySelectorAll('tr').forEach(processRow);
})();
2 Likes

So it’s not actually that simple, which is why I haven’t implemented it in the past.

First, while it’s easy to get a callsign root out of any callsign, say “HB9/VK3ARR/P”, things get weird once you start dealing with worldwide callsigns, in particular callsigns from the UK. Some people use their base callsign - say M3ABC, and others, based in say Wales, will use MW3ABC. However, you chase MW3ABC in Wales and their callsign will be M3ABC in the database, so the link doesn’t work. Or perhaps they live in Scotland and MW3ABC converts to MM3ABC in the database. Then you have all the folks with /P in their database callsign (although we’re making progress on fixing those up) - so you pull up the root callsign, but actually that’s not the root callsign you were looking for. All up, very messy, and while I’ll probably implement it for now, if it leads to a lot of “hey, this link doesn’t work” requests to the MT, I’ll back it out. Let’s not even go into those using special event callsigns or club callsigns.

The second one, the link to the actual activation, similarly, relies on the activator actually submitting the log for that link to be valid and able to be found, which is highly dependent on when activators submit logs - some do it about every three to four months (you know who you are!).

1 Like

Thanks for the detailed explanation, Andrew. And of course you know the API and the database internals far better than I do, so please take this just as a thought from someone tinkering on the outside.

When I was experimenting with the API for my own purposes, I noticed that lookups seem to resolve to a numeric user ID rather than the callsign string itself. Would it be feasible to use that ID as the anchor for the profile/log link? My thinking is that alternative callsigns (not just prefixes like HB9/, but also regional variants like MW3ABC vs. M3ABC) should all resolve to the same user ID on the backend – so the normalization problem might effectively disappear if the link targets the ID rather than the callsign.

As for the link to the actual activation log: couldn’t that simply only be rendered if the log actually exists? If the activator hasn’t submitted yet, there’s just no link and thus no broken URL. The callsign alone (linking to the profile/log overview) would already be a huge improvement over nothing.

I’ll admit I have no idea how the API references individual activation logs internally, so maybe that second part is the harder problem. Anyway, just wanted to throw it out there. Thanks for taking the time to explain the challenges and for giving it some thought!

1 Like

Unfortunately there is not a 1:1 mapping between callsigns and users. Many users have more than one callsign (club calls, etc) and many callsigns have more than one user. We have several instances of people inheriting SK callsigns, as an example. Callsigns are not a unique identifier, which ham radio folks seem to rediscover every few years or so :smiley:

Our existing code that maps is…optimistic..in its mapping at best.

1 Like

Fair point. Thanks for the insight. Sounds like one of those problems that looks trivial until you actually dig into the edge cases. I’ll stop pretending it’s easy from the outside. :grinning_face:

1 Like