Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testpr #479

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/prcomment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
on:
pull_request:
types: [opened, reopened]

jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'A deployment is being made to https://ml-machine-${{github.event.pull_request.number}}.r59q.com/. This site will be continuously updated as changes are made to this PR.'
})
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
services:
ml:
build: .
ports:
- "5174:8080"
25 changes: 25 additions & 0 deletions dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# A Dockerfile must begin with a FROM instruction.
# New base image will be built based on the Node.js version 20
# Image that is based on Debian Linux.
FROM node:20

# Create app directory
WORKDIR /usr/src/app

# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./

# Install app dependencies
RUN npm install -g express
RUN npm install

# Bundle app source
COPY . .

# Creates a "dist" folder with the production build
RUN npm run build

EXPOSE 8080

# Start the server using the production build
CMD [ "node", "dist/main.cjs" ]
13 changes: 7 additions & 6 deletions prepEnv.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
// This script is used to setup different build configurations
// run by ` node prepEnv.js branded ` for ml-machine-branded config
import { copyFile } from 'node:fs/promises';
import { copyFile } from 'fs';

// Validate input
const args = process.argv;
Expand All @@ -31,6 +31,7 @@ const fileMoveTargets = {
['./src/__viteBuildVariants__/ml-machine-simple/features.json', './features.json']
]
}

const availableTargets = Object.getOwnPropertyNames(fileMoveTargets);
const buildVariantTarget = args[2];
if (!availableTargets.includes(buildVariantTarget)) {
Expand All @@ -41,14 +42,14 @@ if (!availableTargets.includes(buildVariantTarget)) {

// The actual work
const copyFiles = fileMoveTargets[buildVariantTarget];

copyFiles.forEach(element => {
const source = element[0];
const destination = element[1];
copyFile(source, destination).then(() => {
copyFile(source, destination, (err) => {
console.log("Copied ", element[0], " -> ", element[1])
}).catch((err) => {
console.error("Failed to move ", source, " to ", destination)
throw new Error(err)
if (err) {
console.error("Failed to move ", source, " to ", destination)
throw new Error(err)
}
})
});
13 changes: 13 additions & 0 deletions public/main.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const express = require('express');
const path = require('path');
const app = express();

app.use(express.static(path.join(__dirname, '.')));

app.get('/', async (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});

app.listen(8080, () => {
console.log("Server successfully running on port 8080");
});
2 changes: 2 additions & 0 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import { DeviceRequestStates } from './script/stores/connectDialogStore';
import Router from './router/Router.svelte';
import { Feature, getFeature } from './script/FeatureToggles';
import { welcomeLog } from './script/utils/Logger';
welcomeLog();

ConnectionBehaviours.setInputBehaviour(new InputBehaviour());
ConnectionBehaviours.setOutputBehaviour(new OutputBehaviour());
Expand Down
2 changes: 1 addition & 1 deletion src/StaticConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class StaticConfiguration {
};

// Line colors are picked in the order of this array.
public static readonly liveGraphColors = ['#f9808e', '#80f98e', '#808ef9'];
public static readonly liveGraphColors = ['#f9808e', '#80f98e', '#808ef9', "#58355E", "#E0FF4F", "#FF2ECC", "#F28F3B", "#C8553D"];

// Colors to assign to gestures, useful for identifying gestures on graphs.
public static readonly gestureColors = [
Expand Down
32 changes: 16 additions & 16 deletions src/__tests__/classifier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,37 @@
* SPDX-License-Identifier: MIT
*/

import { classifier, gestures } from '../script/stores/Stores';
import { stores } from '../script/stores/Stores';
import TestMLModelTrainer from './mocks/mlmodel/TestMLModelTrainer';

describe('Classifier tests', () => {
test('Changing matrix does not mark model as untrained', async () => {
const gesture = gestures.createGesture('some gesture');
gestures.createGesture('some gesture2');
await classifier.getModel().train(new TestMLModelTrainer(2));
const gesture = stores.getGestures().createGesture('some gesture');
stores.getGestures().createGesture('some gesture2');
await stores.getClassifier().getModel().train(new TestMLModelTrainer(2));

gesture.setLEDOutput(new Array(25).fill(false) as boolean[]);
expect(classifier.getModel().isTrained()).toBe(true);
expect(stores.getClassifier().getModel().isTrained()).toBe(true);
});

test('Adding gesture marks model as untrained', async () => {
gestures.createGesture('some gesture');
gestures.createGesture('some gesture2');
await classifier.getModel().train(new TestMLModelTrainer(2));
stores.getGestures().createGesture('some gesture');
stores.getGestures().createGesture('some gesture2');
await stores.getClassifier().getModel().train(new TestMLModelTrainer(2));

gestures.createGesture('Added gesture');
stores.getGestures().createGesture('Added gesture');

expect(classifier.getModel().isTrained()).toBe(false);
expect(stores.getClassifier().getModel().isTrained()).toBe(false);
});

test('Removing gesture marks model as untrained', async () => {
gestures.createGesture('some gesture');
gestures.createGesture('some gesture2');
const gesture3 = gestures.createGesture('some gesture2');
await classifier.getModel().train(new TestMLModelTrainer(2));
stores.getGestures().createGesture('some gesture');
stores.getGestures().createGesture('some gesture2');
const gesture3 = stores.getGestures().createGesture('some gesture2');
await stores.getClassifier().getModel().train(new TestMLModelTrainer(2));

gestures.removeGesture(gesture3.getId());
stores.getGestures().removeGesture(gesture3.getId());

expect(classifier.getModel().isTrained()).toBe(false);
expect(stores.getClassifier().getModel().isTrained()).toBe(false);
});
});
63 changes: 63 additions & 0 deletions src/__tests__/data-representation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @vitest-environment jsdom
*/
/**
* (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
*
* SPDX-License-Identifier: MIT
*/

import LiveDataBuffer from '../script/domain/LiveDataBuffer';
import LiveData from '../script/domain/stores/LiveData';
import MicrobitAccelerometerLiveData, { MicrobitAccelerometerDataVector } from '../script/livedata/MicrobitAccelerometerData';
import { repeat } from './testUtils';
import { get } from 'svelte/store';
import { LiveDataVector } from '../script/domain/stores/LiveDataVector';
import SmoothedLiveData from '../script/livedata/SmoothedLiveData';
import { smoothNewValue } from '../script/utils/graphUtils';

describe('Data representation tests', () => {
test('Creating accelerometer live data does not throw', () => {
expect(() => {
new MicrobitAccelerometerLiveData(new LiveDataBuffer(10))
}).not.toThrow();
});

test('Number of elements in buffer does not exceed set amount', () => {
const elemsInBuffer = 10;
const liveData = new MicrobitAccelerometerLiveData(new LiveDataBuffer(elemsInBuffer))

repeat(() => liveData.put(new MicrobitAccelerometerDataVector({ x: 0, y: 0, z: 0 })), 20)


expect(() => liveData.getBuffer().getSeries(100, elemsInBuffer)).not.toThrow();
expect(liveData.getBuffer().getSeries(100, 10).length).toEqual(10);

expect(() => liveData.getBuffer().getSeries(100, elemsInBuffer + 1)).toThrow();
})

test("Can extract vectors from live data", () => {
const liveData: LiveData<LiveDataVector> = new MicrobitAccelerometerLiveData(new LiveDataBuffer(10))

repeat(() => liveData.put(new MicrobitAccelerometerDataVector({ x: 1, y: 2, z: 3 })), 20)

expect(() => get(liveData).getVector()).not.toThrow();
expect(get(liveData).getVector()).toEqual([1, 2, 3])
})

test("Test smoothed values", () => {
const liveData: LiveData<MicrobitAccelerometerDataVector> = new MicrobitAccelerometerLiveData(new LiveDataBuffer(20));
const smoothLiveData = new SmoothedLiveData(liveData, 2);

const point1 = new MicrobitAccelerometerDataVector({ x: 3, y: 2, z: 1 });
const point2 = new MicrobitAccelerometerDataVector({ x: 1, y: 2, z: 3 });

liveData.put(point1)
liveData.put(point2)

expect(get(smoothLiveData).getVector()[0]).toBeCloseTo(smoothNewValue(point2.getVector()[0], point1.getVector()[0]), 10)
expect(get(smoothLiveData).getVector()[1]).toBeCloseTo(smoothNewValue(point2.getVector()[1], point1.getVector()[1]), 10)
expect(get(smoothLiveData).getVector()[2]).toBeCloseTo(smoothNewValue(point2.getVector()[2], point1.getVector()[2]), 10)
})
});

24 changes: 12 additions & 12 deletions src/__tests__/gestures.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,38 @@
* SPDX-License-Identifier: MIT
*/

import { gestures } from '../script/stores/Stores';
import { stores } from "../script/stores/Stores";

describe('Tests of Gestures', () => {
beforeEach(() => {
gestures.clearGestures();
stores.getGestures().clearGestures();
});
test('Creating gesture does not throw', () => {
expect(() => {
gestures.createGesture('test');
stores.getGestures().createGesture('test');
}).not.toThrow();
});

test('Creating 2 gestures makes total number of gesture 2', () => {
gestures.createGesture('Gesture1');
gestures.createGesture('Gesture2');
stores.getGestures().createGesture('Gesture1');
stores.getGestures().createGesture('Gesture2');

expect(gestures.getGestures().length).toBe(2);
expect(stores.getGestures().getGestures().length).toBe(2);
});

test('Can get gesture after creation', () => {
const gestureName = 'test1234';
const gesture = gestures.createGesture(gestureName);
const fetchedGesture = gestures.getGesture(gesture.getId());
const gesture = stores.getGestures().createGesture(gestureName);
const fetchedGesture = stores.getGestures().getGesture(gesture.getId());
expect(fetchedGesture.getName()).toBe(gestureName);
});

test('Clearing gestures clears gestures', () => {
gestures.createGesture('gestureName');
gestures.createGesture('gestureName');
stores.getGestures().createGesture('gestureName');
stores.getGestures().createGesture('gestureName');

gestures.clearGestures();
stores.getGestures().clearGestures();

expect(gestures.getGestures().length).toBe(0);
expect(stores.getGestures().getGestures().length).toBe(0);
});
});
10 changes: 10 additions & 0 deletions src/__tests__/testUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* (c) 2023, Center for Computational Thinking and Design at Aarhus University and contributors
*
* SPDX-License-Identifier: MIT
*/
export const repeat = (func: (a?: any) => any, n: number) => {
for (let i = 0; i < n; i++) {
func();
}
}
10 changes: 7 additions & 3 deletions src/components/3d-inspector/View3DLive.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<script lang="ts">
import SmoothedLiveData from '../../script/livedata/SmoothedLiveData';
import { liveAccelerometerData } from '../../script/stores/Stores';
import { stores } from '../../script/stores/Stores';
import View3D from './View3D.svelte';
import { Vector3 } from './View3DUtility';

Expand All @@ -17,11 +17,15 @@

let liveDataPoint: Vector3 = { x: 0, y: 0, z: 0 };

const smoothedLiveData = new SmoothedLiveData(liveAccelerometerData, 3);
$: smoothedLiveData = new SmoothedLiveData($stores.liveData, 3);

$: {
if (!freeze) {
liveDataPoint = $smoothedLiveData;
liveDataPoint = {
x: $smoothedLiveData.getVector()[0],
y: $smoothedLiveData.getVector()[1],
z: $smoothedLiveData.getVector()[2],
};
}
}
</script>
Expand Down
12 changes: 7 additions & 5 deletions src/components/Gesture.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
import ImageSkeleton from './skeletonloading/ImageSkeleton.svelte';
import GestureTilePart from './GestureTilePart.svelte';
import StaticConfiguration from '../StaticConfiguration';
import { gestures, liveAccelerometerData } from '../script/stores/Stores';
import Gesture from '../script/domain/stores/gesture/Gesture';
import { RecordingData } from '../script/domain/stores/gesture/Gestures';
import { stores } from '../script/stores/Stores';

// Variables for component
export let onNoMicrobitSelect: () => void;
export let gesture: Gesture;
const gestures = stores.getGestures();
$: liveData = $stores.liveData;

const defaultNewName = $t('content.data.classPlaceholderNewClass');
const recordingDuration = StaticConfiguration.recordingDuration;
Expand Down Expand Up @@ -73,10 +75,10 @@
let newData: { x: number[]; y: number[]; z: number[] } = { x: [], y: [], z: [] };

// Set timeout to allow recording in 1s
const unsubscribe = liveAccelerometerData.subscribe(data => {
newData.x.push(data.x);
newData.y.push(data.y);
newData.z.push(data.z);
const unsubscribe = liveData.subscribe(data => {
newData.x.push(data.getVector()[0]);
newData.y.push(data.getVector()[1]);
newData.z.push(data.getVector()[2]);
});

// Once duration is over (1000ms default), stop recording
Expand Down
4 changes: 3 additions & 1 deletion src/components/NewGestureButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
<script lang="ts">
import { areActionsAllowed } from '../script/stores/uiStore';
import { t } from '../i18n';
import { gestures } from '../script/stores/Stores';
import { stores } from '../script/stores/Stores';

const gestures = stores.getGestures();

const defaultNewName = $t('content.data.classPlaceholderNewClass');

Expand Down
4 changes: 2 additions & 2 deletions src/components/bottom/ConnectedLiveGraphButtons.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
<script lang="ts">
import { t } from '../../i18n';
import TypingUtils from '../../script/TypingUtils';
import { classifier } from '../../script/stores/Stores';
import { stores } from '../../script/stores/Stores';
import { state } from '../../script/stores/uiStore';
import StandardButton from '../buttons/StandardButton.svelte';

export let onOutputDisconnectButtonClicked: () => void;
export let onOutputConnectButtonClicked: () => void;
export let onInputDisconnectButtonClicked: () => void;

const model = classifier.getModel();
const model = stores.getClassifier().getModel();
</script>

<!-- These are the buttons that are present while the input micro:bit is connected-->
Expand Down
Loading
Loading