Skip to content

Commit b4db5c9

Browse files
authored
Merge pull request #655 from microbit-foundation/main
Fingerpints, validate, and more...
2 parents 01256be + 6ca2255 commit b4db5c9

File tree

98 files changed

+1744
-710
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+1744
-710
lines changed

.github/workflows/math.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# This workflow uses actions that are not certified by GitHub.
2+
# They are provided by a third-party and are governed by
3+
# separate terms of service, privacy policy, and support
4+
# documentation.
5+
6+
# GitHub recommends pinning actions to a commit SHA.
7+
# To get a newer version, you will need to update the SHA.
8+
# You can also reference a tag or branch, but the action may change without warning.
9+
10+
name: Testing deployment
11+
12+
env:
13+
APP_LOCATION: "/" # location of your client code
14+
APP_ARTIFACT_LOCATION: "/dist" # location of client code build output
15+
16+
on:
17+
push:
18+
branches:
19+
- production-math
20+
pull_request:
21+
types: [opened, synchronize, reopened, closed]
22+
branches:
23+
- production-math
24+
25+
permissions:
26+
issues: write
27+
contents: read
28+
29+
jobs:
30+
test:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v3
34+
- name: Run Tests
35+
run: |
36+
npm ci
37+
npm test
38+
formatting_and_linting:
39+
name: Prettier / svelte Check
40+
runs-on: ubuntu-latest
41+
steps:
42+
- name: Checkout Repository
43+
uses: actions/checkout@v3
44+
- name: Run prettier
45+
shell: bash
46+
run: |
47+
npm ci
48+
npm run checkFormat
49+
- name: Svelte check
50+
shell: bash
51+
run: npm run check
52+
53+
build_and_deploy:
54+
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
55+
runs-on: ubuntu-latest
56+
name: Build and Deploy
57+
steps:
58+
- uses: actions/checkout@v3
59+
with:
60+
submodules: true
61+
- name: Build And Deploy
62+
uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9
63+
with:
64+
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_PRODUCTION_MATH_TOKEN }}
65+
repo_token: ${{ secrets.GITHUB_TOKEN }}
66+
action: "upload"
67+
app_location: ${{ env.APP_LOCATION }}
68+
app_artifact_location: ${{ env.APP_ARTIFACT_LOCATION }}
69+
70+
close:
71+
if: github.event_name == 'pull_request' && github.event.action == 'closed'
72+
runs-on: ubuntu-latest
73+
name: Close
74+
steps:
75+
- name: Close
76+
uses: Azure/static-web-apps-deploy@1a947af9992250f3bc2e68ad0754c0b0c11566c9
77+
with:
78+
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_PRODUCTION_MATH_TOKEN }}
79+
action: "close"

features.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
"makecode": true,
66
"liveGraphInputValues": true,
77
"recordingScrubberValues": true,
8-
"modelValidation": true
8+
"modelValidation": true,
9+
"modelSettings": true,
10+
"fingerprint": true
911
}

src/App.svelte

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import Router from './router/Router.svelte';
3131
import { Feature, getFeature } from './lib/FeatureToggles';
3232
import { welcomeLog } from './lib/utils/Logger';
33-
import { DeviceRequestStates, state } from './lib/stores/Stores';
3433
import MediaQuery from './components/layout/MediaQuery.svelte';
3534
import BottomBarMenuView from './components/layout/BottomBarMenuView.svelte';
3635
import CookieBanner from './components/features/cookie-bannner/CookieBanner.svelte';
@@ -39,11 +38,17 @@
3938
import OverlayView from './components/layout/OverlayView.svelte';
4039
import SideBarMenuView from './components/layout/SideBarMenuView.svelte';
4140
import PageContentView from './components/layout/PageContentView.svelte';
41+
import { stores } from './lib/stores/Stores';
42+
import { DeviceRequestStates } from './lib/domain/Devices';
43+
import { isLoading } from './lib/stores/ApplicationState';
44+
45+
const devices = stores.getDevices();
46+
4247
welcomeLog();
4348
4449
if (CookieManager.isReconnectFlagSet()) {
45-
$state.offerReconnect = true;
46-
$state.reconnectState = DeviceRequestStates.INPUT;
50+
$devices.offerReconnect = true;
51+
$devices.reconnectState = DeviceRequestStates.INPUT;
4752
CookieManager.unsetReconnectFlag();
4853
}
4954
@@ -56,7 +61,7 @@
5661
<!-- Denies mobile users access to the platform -->
5762
<IncompatiblePlatformView />
5863
{:else}
59-
{#if $state.isLoading}
64+
{#if $isLoading}
6065
<main class="h-screen w-screen bg-primary flex absolute z-10" transition:fade>
6166
<LoadingSpinner />
6267
</main>

src/StaticConfiguration.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,5 +148,10 @@ class StaticConfiguration {
148148
* Whether the knn model should be normalized by default
149149
*/
150150
public static readonly knnNormalizedDefault = false;
151+
152+
/**
153+
* Whether fingerprinting should be enabled by default
154+
*/
155+
public static readonly enableFingerprintByDefault: boolean = false;
151156
}
152157
export default StaticConfiguration;

src/__tests__/csv/csv.test.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @vitest-environment jsdom
3+
*/
4+
/**
5+
* (c) 2023-2025, Center for Computational Thinking and Design at Aarhus University and contributors
6+
*
7+
* SPDX-License-Identifier: MIT
8+
*/
9+
10+
import { writable } from 'svelte/store';
11+
import type { RecordingData } from '../../lib/domain/RecordingData';
12+
import Gesture from '../../lib/domain/stores/gesture/Gesture';
13+
import type { PersistedGestureData } from '../../lib/domain/stores/gesture/Gestures';
14+
import type GestureConfidence from '../../lib/domain/stores/gesture/GestureConfidence';
15+
import {
16+
serializeGestureRecordingsToCSV,
17+
serializeRecordingToCsvWithoutGestureName,
18+
} from '../../lib/utils/CSVUtils';
19+
20+
describe('CSV Test', () => {
21+
// A crude way to enforce direction of dependencies, inspired by ArchUnit for java
22+
test('Convert recording', () => {
23+
const input: RecordingData = {
24+
ID: 123,
25+
labels: ['x', 'y', 'z'],
26+
samples: [
27+
{
28+
vector: [1, 2, 3],
29+
},
30+
{
31+
vector: [4, 5, 6],
32+
},
33+
{
34+
vector: [7, 8, 9],
35+
},
36+
],
37+
};
38+
const data = writable({
39+
recordings: [input],
40+
name: 'Test;Gesture',
41+
} as PersistedGestureData);
42+
const confidence = writable({}) as unknown as GestureConfidence;
43+
const gesture: Gesture = new Gesture(data, confidence, () => void 0);
44+
const result = serializeGestureRecordingsToCSV([gesture]);
45+
expect(result).toBe(
46+
'gesture;sample;x;y;z\nTest\\;Gesture;0;1;2;3\nTest\\;Gesture;1;4;5;6\nTest\\;Gesture;2;7;8;9',
47+
);
48+
});
49+
50+
test('Convert multiple gestures', () => {
51+
const input1: RecordingData = {
52+
ID: 123,
53+
labels: ['x', 'y', 'z'],
54+
samples: [
55+
{
56+
vector: [1, 2, 3],
57+
},
58+
{
59+
vector: [4, 5, 6],
60+
},
61+
],
62+
};
63+
const input2: RecordingData = {
64+
ID: 456,
65+
labels: ['x', 'y', 'z'],
66+
samples: [
67+
{
68+
vector: [7, 8, 9],
69+
},
70+
{
71+
vector: [10, 11, 12],
72+
},
73+
],
74+
};
75+
const data1 = writable({
76+
recordings: [input1],
77+
name: 'Gesture1',
78+
} as PersistedGestureData);
79+
const data2 = writable({
80+
recordings: [input2],
81+
name: 'Gesture2',
82+
} as PersistedGestureData);
83+
const confidence = writable({}) as unknown as GestureConfidence;
84+
const gesture1: Gesture = new Gesture(data1, confidence, () => void 0);
85+
const gesture2: Gesture = new Gesture(data2, confidence, () => void 0);
86+
const result = serializeGestureRecordingsToCSV([gesture1, gesture2]);
87+
expect(result).toBe(
88+
'gesture;sample;x;y;z\n' +
89+
'Gesture1;0;1;2;3\n' +
90+
'Gesture1;1;4;5;6\n' +
91+
'Gesture2;0;7;8;9\n' +
92+
'Gesture2;1;10;11;12',
93+
);
94+
});
95+
96+
test('Serialize recording without gesture name (with headers)', () => {
97+
const input: RecordingData = {
98+
ID: 123,
99+
labels: ['x', 'y', 'z'],
100+
samples: [
101+
{
102+
vector: [1, 2, 3],
103+
},
104+
{
105+
vector: [4, 5, 6],
106+
},
107+
{
108+
vector: [7, 8, 9],
109+
},
110+
],
111+
};
112+
113+
const result = serializeRecordingToCsvWithoutGestureName(input);
114+
expect(result).toBe('sample;x;y;z\n' + '0;1;2;3\n' + '1;4;5;6\n' + '2;7;8;9');
115+
});
116+
});

src/__tests__/mocks/mlmodel/TestMLModelTrainer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
*
44
* SPDX-License-Identifier: MIT
55
*/
6+
import type { ModelInfo } from '../../../lib/domain/ModelRegistry';
7+
import ModelRegistry from '../../../lib/domain/ModelRegistry';
68
import type { ModelTrainer } from '../../../lib/domain/ModelTrainer';
79
import type { TrainingDataRepository } from '../../../lib/domain/TrainingDataRepository';
810
import TestMLModel from './TestMLModel';
911

1012
class TestMLModelTrainer implements ModelTrainer<TestMLModel> {
1113
constructor(private numberOfGestures: number) {}
14+
getModelInfo(): ModelInfo {
15+
return ModelRegistry.NeuralNetwork;
16+
}
1217
public trainModel(trainingData: TrainingDataRepository): Promise<TestMLModel> {
1318
return Promise.resolve(new TestMLModel(this.numberOfGestures));
1419
}

src/__viteBuildVariants__/ml-machine-simple/features.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
"makecode": false,
66
"liveGraphInputValues": false,
77
"recordingScrubberValues": false,
8-
"modelValidation": false
8+
"modelValidation": false,
9+
"modelSettings": false,
10+
"fingerprint": false
911
}

src/__viteBuildVariants__/ml-machine/features.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
"makecode": true,
66
"liveGraphInputValues": true,
77
"recordingScrubberValues": true,
8-
"modelValidation": true
8+
"modelValidation": true,
9+
"modelSettings": true,
10+
"fingerprint": true
911
}

src/__viteBuildVariants__/unbranded/features.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
"makecode": true,
66
"liveGraphInputValues": true,
77
"recordingScrubberValues": true,
8-
"modelValidation": true
8+
"modelValidation": true,
9+
"modelSettings": true,
10+
"fingerprint": true
911
}

src/assets/messages/ui.da.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"content.data.noData.exampleName.shake": "Ryste",
5151
"content.data.noData.exampleName.still": "Stille",
5252
"content.data.noData.exampleName.circle": "Cirkel",
53+
"content.data.tooltip.remove": "Fjern",
5354
"content.trainer.failure.header": "Træning mislykkedes",
5455
"content.trainer.failure.body": "Træningen resulterede ikke i en brugbar model. Grunden til dette ligger sandsynligvis i dataet. Hvis dataet i forskellige klasser minder for meget om hinanden, kan dette resultere i nogle forskellige problemer i træningsprocessen, der ikke gør det muligt at træne modellen ordentligt.",
5556
"content.trainer.failure.todo": "Gå tilbage til datasiden og ændr i din data.",
@@ -111,8 +112,9 @@
111112
"content.validation.infobox.classesContent": "Her kan du se de klasser, du har defineret på datasiden.",
112113
"content.validation.noGestures.title": "Ingen klasser...",
113114
"content.validation.noGestures.description": "Du har endnu ikke tilføjet nogen klasser. Gå til datatrinnet i menuen til venstre og tilføj nogle.",
114-
"content.validation.tutorial.title": "Validierungssätze",
115-
"content.validation.tutorial.description": "Erstelle einen separaten Datensatz, um die Leistung deines Modells zu bewerten. Das Modell wird nicht mit diesen Daten trainiert.",
115+
"content.validation.tutorial.title": "Valideringssæt",
116+
"content.validation.tutorial.description": "Opret et separat datasæt til at evaluere dit models ydeevne. Modellen vil ikke blive trænet på disse data.",
117+
"content.validation.tutorial.trainmodelfirst": "Zuerst ein Modell trainieren",
116118
"footer.connectButtonNotConnected": "Tilslut din BBC micro:bit",
117119
"footer.disconnectButton": "Frakobl",
118120
"footer.helpHeader": "Live graf",

0 commit comments

Comments
 (0)