Skip to content

Commit e01fb4e

Browse files
authored
Merge pull request #581 from Mattk70/Development
Add version tracking infrastructure and refactor tracking API
2 parents 6c599c3 + a9c0cf2 commit e01fb4e

11 files changed

Lines changed: 95 additions & 127 deletions

File tree

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ <h6>Low Shelf filter</h6>
12781278
</div>
12791279
<label for="attenuation" class="form-label">Attenuation:</label>
12801280
<div class="input-group me-0 p-0">
1281-
<input type="range" class="form-control-range w-75" min="0" max="50" id="attenuation">
1281+
<input type="range" class="form-control-range w-75" min="0" max="100" id="attenuation">
12821282
<div class="input-group-append">
12831283
<span class="input-group-text" id="attenuation-threshold">0 dB</span>
12841284
</div>

js/components/spectrogram.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -983,8 +983,7 @@ export class ChirpityWS {
983983
const key = moveDirection > 0 ? "PageDown" : "PageUp";
984984
config.debug && console.log(`scrolling x: ${e.deltaX} y: ${e.deltaY}`);
985985
this.actions[key](e);
986-
this.handlers.trackEvent(config.UUID, "Swipe", key, "");
986+
this.handlers.trackEvent({uuid: config.UUID, event: "Swipe", action: key, version: config.VERSION});
987987
}
988988
}
989989

990-

js/models/BirdNet2.4.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ onmessage = async (e) => {
6363
console.log(tf.env().getFlags());
6464
}
6565
Model = new BirdNETModel(appPath, version);
66-
Model.UUID = data.UUID
66+
Model.UUID = data.UUID;
67+
Model.VERSION = data.version;
6768
Model.labels = labels;
6869
// Prepare a mask to squash 'background' predictions
6970
const bgIndex = labels.findIndex(item => item.toLowerCase().includes('background'));

js/models/training.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ async function trainModel({
6363
dataset, cache:cacheFolder, modelLocation:saveLocation, modelType,
6464
useCache, validation, mixup, decay,
6565
useWeights, useFocal, useNoise, labelSmoothing}) {
66-
installConsoleTracking(() => Model.UUID, "Training");
66+
installConsoleTracking(() => [Model.UUID, Model.VERSION], "Training");
6767
const {files:allFiles, classWeights} = getFilesWithLabelsAndWeights(dataset);
6868
i18n = messages[locale] || messages['en'];
6969
if (!allFiles.length){

js/ui.js

Lines changed: 49 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ let LOCATIONS, pagination,
3737
loadingTimeout,
3838
LIST_MAP;
3939

40+
let VERSION;
4041
let APPLICATION_LOADED = false;
4142
let LABELS = [],
4243
HISTORY = [];
@@ -49,10 +50,11 @@ window.addEventListener("unhandledrejection", function (event) {
4950

5051
// Track the unhandled promise rejection
5152
trackEvent(
52-
config.UUID,
53-
"Unhandled UI PR",
54-
errorMessage,
55-
customURLEncode(stackTrace)
53+
{uuid:config.UUID,
54+
event: "Unhandled UI PR",
55+
action: errorMessage,
56+
name: customURLEncode(stackTrace),
57+
version: config.VERSION}
5658
);
5759
});
5860

@@ -64,10 +66,11 @@ window.addEventListener("rejectionhandled", function (event) {
6466

6567
// Track the unhandled promise rejection
6668
trackEvent(
67-
config.UUID,
68-
"Handled UI PR",
69-
errorMessage,
70-
customURLEncode(stackTrace)
69+
{uuid:config.UUID,
70+
event: "Handled UI PR",
71+
action: errorMessage,
72+
name: customURLEncode(stackTrace),
73+
version: config.VERSION}
7174
);
7275
});
7376

@@ -346,7 +349,7 @@ window.electron.onFileOpen((filePath) => {
346349
// Is this CI / playwright?
347350
const isTestEnv = window.env.TEST_ENV === "true";
348351
const trackVisit = isTestEnv ? () => {} : _trackVisit;
349-
isTestEnv || installConsoleTracking(() => config.UUID, "UI");
352+
isTestEnv || installConsoleTracking(() => [config.UUID, config.VERSION], "UI");
350353
const trackEvent = isTestEnv ? () => {} : _trackEvent;
351354
isTestEnv && console.log("Running in test environment");
352355

@@ -387,7 +390,6 @@ async function getPaths() {
387390
return [appPath, tempPath, locale, dirname];
388391
}
389392

390-
let VERSION;
391393
let DIAGNOSTICS = {};
392394

393395
let appVersionLoaded = new Promise((resolve, reject) => {
@@ -441,7 +443,7 @@ DOM.controlsWrapper.addEventListener("mousedown", (e) => {
441443
// Remove event listener on mouseup
442444
const onMouseUp = () => {
443445
document.removeEventListener("mousemove", onMouseMove);
444-
trackEvent(config.UUID, "Drag", "Spec Resize", newHeight);
446+
trackEvent({uuid: config.UUID, event: "Drag", action: "Spec Resize", value: newHeight, version: config.VERSION});
445447
};
446448
// Attach event listeners for mousemove and mouseup
447449
document.addEventListener("mousemove", onMouseMove);
@@ -466,7 +468,7 @@ chartContainer.addEventListener("mousedown", (e) => {
466468
const onMouseUp = () => {
467469
document.body.style.cursor = "default";
468470
document.removeEventListener("mousemove", onMouseMove);
469-
trackEvent(config.UUID, "Drag", "Chart Resize", newHeight);
471+
trackEvent({uuid: config.UUID, event: "Drag", action: "Chart Resize", value: newHeight, version: config.VERSION});
470472
};
471473
// Attach event listeners for mousemove and mouseup
472474
document.addEventListener("mousemove", onMouseMove);
@@ -745,7 +747,7 @@ function showDatePicker() {
745747
start: timestamp,
746748
refreshResults: STATE.analysisDone, // Only refresh results if analysis has already been done
747749
});
748-
trackEvent(config.UUID, "Settings Change", "fileStart", newStart);
750+
trackEvent({uuid: config.UUID, event: "Settings Change", action: "fileStart", name: newStart, version: config.VERSION});
749751
resetResults();
750752
STATE.fileStart = timestamp;
751753
// Remove the form from the DOM
@@ -2054,10 +2056,7 @@ window.onload = async () => {
20542056
// Attach an error event listener to the window object
20552057
window.onerror = function (message, file, lineno, colno, error) {
20562058
trackEvent(
2057-
config.UUID,
2058-
"Error",
2059-
error.message,
2060-
encodeURIComponent(error.stack)
2059+
{uuid: config.UUID, event: "Error", action: error.message, name: encodeURIComponent(error.stack), version: config.VERSION}
20612060
);
20622061
// Return false not to inhibit the default error handling
20632062
return false;
@@ -2129,6 +2128,7 @@ window.onload = async () => {
21292128
debug,
21302129
fileStartMtime,
21312130
specDetections,
2131+
VERSION
21322132
});
21332133
t0_warmup = Date.now();
21342134
if (isTestEnv) {
@@ -3069,7 +3069,7 @@ function handleKeyDown(e) {
30693069
: e.metaKey
30703070
? "Alt"
30713071
: "no";
3072-
trackEvent(config.UUID, "KeyPress", action, modifier);
3072+
trackEvent({uuid: config.UUID, event: "KeyPress", action, name:modifier, version: config.VERSION});
30733073
GLOBAL_ACTIONS[action](e);
30743074
} else {
30753075
GLOBAL_ACTIONS.handleNumberKeys(e);
@@ -3920,37 +3920,25 @@ function onAnalysisComplete({ quiet }) {
39203920
? STATE.selection.end - STATE.selection.start
39213921
: DIAGNOSTICS["Audio Duration"];
39223922
const rate = duration / parseFloat(analysisTime);
3923-
3923+
if (rate > 10_000) return // must be a database fetch - skip the diagnostics which are only relevant to audio analysis
3924+
const backend = config.models[config.selectedModel].backend;
3925+
const event = `${config.selectedModel}-${backend}`
39243926
trackEvent(
3925-
config.UUID,
3926-
`${config.selectedModel}-${config.models[config.selectedModel].backend}`,
3927-
"Audio Duration",
3928-
config.models[config.selectedModel].backend,
3929-
Math.round(duration)
3927+
{uuid:config.UUID, event, action:"Audio Duration", name: backend, value:Math.round(duration), version: config.VERSION}
39303928
);
3931-
39323929
if (!STATE.selection) {
39333930
trackEvent(
3934-
config.UUID,
3935-
`${config.selectedModel}-${config.models[config.selectedModel].backend}`,
3936-
"Analysis Rate",
3937-
config.models[config.selectedModel].backend,
3938-
parseInt(rate)
3931+
{uuid: config.UUID, event, action: "Analysis Rate", name: backend, value: parseInt(rate), version: config.VERSION}
39393932
);
39403933
trackEvent(
3941-
config.UUID,
3942-
`${config.selectedModel}-${config.models[config.selectedModel].backend}`,
3943-
"Analysis Duration",
3944-
config.models[config.selectedModel].backend,
3945-
parseInt(analysisTime)
3934+
{uuid: config.UUID, event, action: "Analysis Duration", name: backend, value: parseInt(analysisTime), version: config.VERSION}
39463935
);
39473936
DIAGNOSTICS["Analysis Duration"] = utils.formatDuration(analysisTime);
39483937
DIAGNOSTICS["Analysis Rate"] =
39493938
rate.toFixed(0) + "x faster than real time performance.";
39503939
generateToast({ message: "complete" });
39513940
displayProgress({percent: 100});
39523941
}
3953-
worker.postMessage({ action: "update-state", selection: false });
39543942
}
39553943

39563944
function removeNoEntry() {
@@ -5543,6 +5531,7 @@ async function handleUIClicks(e) {
55435531
break;
55445532
}
55455533
case "clear-database-location": {
5534+
if (PREDICTING) break;
55465535
config.database.location = undefined;
55475536
document.getElementById("database-location").value = "";
55485537
worker.postMessage({
@@ -6143,7 +6132,7 @@ async function handleUIClicks(e) {
61436132
config.debug && console.log("clicked", target);
61446133
target &&
61456134
target !== "result1" &&
6146-
trackEvent(config.UUID, "UI", "Click", target);
6135+
trackEvent({uuid:config.UUID, event: "UI", action: "Click", name: target, version: VERSION});
61476136
};
61486137

61496138
/**
@@ -6710,7 +6699,7 @@ document.addEventListener("change", async function (e) {
67106699
updatePrefs("config.json", config);
67116700
const value = element.type === "checkbox" ? element.checked : element.value;
67126701
target === "fileStart" ||
6713-
trackEvent(config.UUID, "Settings Change", target, value);
6702+
trackEvent({uuid:config.UUID, event: "Settings Change", action: target, value: value, version: VERSION});
67146703
}
67156704
});
67166705

@@ -6830,7 +6819,7 @@ async function readLabels(labelFile, updating) {
68306819
refreshResults: STATE.analysisDone && STATE.mode !== 'chart',
68316820
member: STATE.isMember,
68326821
});
6833-
trackEvent(config.UUID, "UI", "Create", "Custom list", labels.length);
6822+
trackEvent({uuid:config.UUID, event: "UI", action: "Create", name: "Custom list", value: labels.length, version: VERSION});
68346823
} else {
68356824
LABELS = labels;
68366825
worker.postMessage({
@@ -6909,9 +6898,7 @@ async function createContextMenu(e) {
69096898
return;
69106899
} else if (target.classList.contains("circle") || target.closest("thead"))
69116900
return;
6912-
let hideInSummary = "",
6913-
hideInSelection = "",
6914-
plural = "";
6901+
let hideInSummary = "", hideInSelection = "", disableWhenPredicting = "", plural = "";
69156902
const inSummary = target.closest("#speciesFilter");
69166903
const resultContext = !target.closest("#summaryTable");
69176904
if (inSummary) {
@@ -6920,6 +6907,7 @@ async function createContextMenu(e) {
69206907
} else if (target.closest("#selectionResultTableBody")) {
69216908
hideInSelection = "d-none";
69226909
}
6910+
if (PREDICTING) disableWhenPredicting = "disabled";
69236911
const hideFindSimilar = (['analyse', 'archive']).includes(STATE.mode) && ! ['chirpity', 'nocmig'].includes(config.selectedModel) ? '' : 'd-none';
69246912
// If we haven't clicked the active row or we cleared the region, load the row we clicked
69256913
if (resultContext || hideInSelection || hideInSummary) {
@@ -6942,36 +6930,28 @@ async function createContextMenu(e) {
69426930
DOM.contextMenu.innerHTML = `
69436931
<div id="${inSummary ? "inSummary" : "inResults"}">
69446932
<ul class="list-unstyled mb-1">
6945-
<li class="dropdown-item ${hideInSummary}" id="play-region"><span class='material-symbols-outlined'>play_circle</span> ${
6946-
i18.play
6947-
}</li>
6948-
<li class="dropdown-item ${hideInSummary} ${hideInSelection}" id="context-analyse-selection">
6949-
<span class="material-symbols-outlined">search</span> ${i18.analyse}
6933+
<li class="dropdown-item ${hideInSummary}" id="play-region"><span class='material-symbols-outlined'>play_circle</span> ${i18.play}</li>
6934+
<li class="dropdown-item ${hideInSummary} ${hideInSelection} ${disableWhenPredicting}" id="context-analyse-selection">
6935+
<span class="material-symbols-outlined">search</span> ${i18.analyse}
69506936
</li>
6951-
<li class="dropdown-item ${hideFindSimilar} ${hideInSummary} ${hideInSelection} ${disabled}" id="context-find-similar">
6952-
<span class="material-symbols-outlined">search</span> ${i18.find}
6937+
<li class="dropdown-item ${disableWhenPredicting} ${hideFindSimilar} ${hideInSummary} ${hideInSelection} ${disabled}" id="context-find-similar">
6938+
<span class="material-symbols-outlined">search</span> ${i18.find}
69536939
</li>
69546940
<div class="dropdown-divider ${hideInSummary}"></div>
69556941
<li class="dropdown-item ${hideInSelection}" id="create-manual-record">
6956-
<span class="material-symbols-outlined">edit_document</span> ${createOrEdit} ${
6957-
i18.record
6958-
}
6942+
<span class="material-symbols-outlined">edit_document</span> ${createOrEdit} ${i18.record}
69596943
</li>
69606944
<li class="dropdown-item" id="context-create-clip">
6961-
<span class="material-symbols-outlined">music_note</span> ${i18.export}
6945+
<span class="material-symbols-outlined">music_note</span> ${i18.export}
69626946
</li>
6963-
<span class="dropdown-item" id="context-xc" target="xc">
6964-
<img src='img/logo/XC.png' alt='' style="filter:grayscale(100%);height: 1.5em"> ${
6965-
i18.compare
6966-
}
6967-
</span>
6947+
<span class="dropdown-item" id="context-xc" target="xc">
6948+
<img src='img/logo/XC.png' alt='' style="filter:grayscale(100%);height: 1.5em"> ${i18.compare}
6949+
</span>
69686950
<div class="dropdown-divider ${hideInSelection}"></div>
69696951
<li class="dropdown-item ${hideInSelection}" id="context-delete">
6970-
<span class='delete material-symbols-outlined'>delete_forever</span> ${
6971-
i18.delete
6972-
}
6952+
<span class='delete material-symbols-outlined'>delete_forever</span> ${i18.delete}
69736953
</li>
6974-
</ul>
6954+
</ul>
69756955
</div>
69766956
`;
69776957
const modalTitle = document.getElementById("record-entry-modal-label");
@@ -7550,12 +7530,12 @@ async function getXCComparisons() {
75507530
const content = "Loading Xeno-Canto data...";
75517531
loadingFiles({hide:false, content})
75527532
const bats = config.selectedModel.includes('bats');
7553-
const quality = "+q:%22>C%22";
7554-
const defaultLength = bats ? "+len:0.5-10" : "+len:3-15";
7533+
const quality = '+q:">C"';
7534+
const defaultLength = bats ? '+len:"0.5-10"' : '+len:"3-15"';
75557535
sname = XCtaxon[sname] || sname;
75567536
const types = bats
7557-
? ['"distress call"', '"feeding buzz"', '"social call"', 'ecolocation', 'song']
7558-
: ['"nocturnal flight call"', '"flight call"', 'call', 'song'];
7537+
? ['distress call', 'feeding buzz', 'social call', 'echolocation', 'song']
7538+
: ['nocturnal flight call', 'flight call', 'call', 'song'];
75597539
const filteredLists = {}
75607540
types.forEach((type) => {
75617541
filteredLists[type] = []; // Initialize each type with an empty array
@@ -7565,8 +7545,8 @@ async function getXCComparisons() {
75657545
const fetchRequests = types.map((type) => {
75667546
type = type.replaceAll(" ", "%20"); // Replace spaces with entities for the API query
75677547
// Use a different length parameter for "song"
7568-
const typeLength = type === "song" ? "+len:10-30" : defaultLength;
7569-
const query = `https://xeno-canto.org/api/3/recordings?key=d5e2d2775c7f2b2fb8325ffacc41b9e6aa94679e&query=sp:"${sname}"${quality}${typeLength}+type:=${type}`;
7548+
const typeLength = type === "song" ? '+len:"10-30"' : defaultLength;
7549+
const query = `https://xeno-canto.org/api/3/recordings?key=d5e2d2775c7f2b2fb8325ffacc41b9e6aa94679e&query=sp:"${sname}"${quality}${typeLength}+type:"${type}"`;
75707550

75717551
return fetch(query)
75727552
.then((response) =>
@@ -8433,10 +8413,7 @@ function checkForIntelMacUpdates() {
84338413
"warning"
84348414
);
84358415
trackEvent(
8436-
config.UUID,
8437-
"Update message",
8438-
`From ${VERSION}`,
8439-
`To: ${latestVersion}`
8416+
{uuid:config.UUID, event: "Update message", action: `From ${VERSION}`, name: `To: ${latestVersion}`, version: VERSION}
84408417
);
84418418
}
84428419
config.lastUpdateCheck = latestCheck;

js/utils/i18n.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,7 @@ const Context = {
12021202
midnight: "Midnight", noon: 'Noon', one: 'day', other: 'days',
12031203
apply: 'Apply', cancel: 'Cancel', filter: 'Apply a date Filter',
12041204
'nocturnal flight call': 'Nocturnal Flight Call', 'flight call': 'Flight Call', call: 'Call', song: 'Song',
1205-
ecolocation: 'Echolocation', 'feeding buzz': 'Feeding Buzz', 'distress call': 'Distress Call', 'social call': 'Social Call',
1205+
echolocation: 'Echolocation', 'feeding buzz': 'Feeding Buzz', 'distress call': 'Distress Call', 'social call': 'Social Call',
12061206
play: 'Play',
12071207
pause: 'Pause',
12081208
analyse: 'Analyse',

js/utils/importer.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ async function countLines(filePath) {
3939
files: new Map(),
4040
fileExists: new Map()
4141
};
42+
43+
// Set up the model cache initially
44+
const models = await db.allAsync('SELECT id, name FROM MODELS');
45+
models.forEach(m => caches.models.set(m.name, m.id))
46+
4247
const processingTasks = [];
4348
const totalLines = await countLines(file);
4449
let rowCounter = 0, lastPercentReported = -1;
@@ -79,10 +84,10 @@ async function countLines(filePath) {
7984
} catch (error) {
8085
return reject(error)
8186
}
82-
let exists = caches.fileExists.get(file);
87+
let exists = caches.fileExists.get(row.file);
8388
if (exists === undefined) {
84-
exists = fs.existsSync(file);
85-
caches.fileExists.set(file, exists);
89+
exists = fs.existsSync(row.file);
90+
caches.fileExists.set(row.file, exists);
8691
}
8792
if (exists) {
8893
fileSet.add(row.file);

js/utils/state.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ export class WorkerState {
8484
(this.notFound = {}), // try to prevent spamming errors
8585
(this.local = true),
8686
(this.incrementor = 2),
87-
(this.UUID = 0),
87+
(this.UUID = null),
88+
(this.VERSION = 0),
8889
(this.track = true),
8990
(this.library = {
9091
location: undefined,

0 commit comments

Comments
 (0)