Skip to content

Commit

Permalink
refactor: WPMCalculator, rename stuff in shorter metric form (ms, min…
Browse files Browse the repository at this point in the history
…, etc.) and add test
  • Loading branch information
MhouneyLH committed Jan 21, 2024
1 parent 492c4e5 commit a567bee
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 133 deletions.
11 changes: 0 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,6 @@
"command": "keystrokemanager.mostOftenPressedKeys",
"title": "Keystrokes: Show most often pressed keys"
}
],
"configuration": [
{
"title": "KeystrokeManager",
"properties": {
"keystrokeManager.configurationFileName": {
"type": "string",
"default": "keystrokeManagerConfig.json"
}
}
}
]
},
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from "vscode";

import { SECOND_AS_MILLISECONDS } from "./libs/constants";
import { SECOND_IN_MS } from "./libs/constants";
import { getPressedKey, isValidChangedContent } from "./libs/utils";
import {
keystrokeRepository,
Expand Down Expand Up @@ -93,7 +93,7 @@ function createStatusBarItems(subscriptions: any): void {

setInterval(() => {
wpmStatusBar.update();
}, SECOND_AS_MILLISECONDS);
}, SECOND_IN_MS);
}

function updateKeystrokes(event: vscode.TextDocumentChangeEvent): void {
Expand Down
5 changes: 3 additions & 2 deletions src/libs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export const SECOND_ICON = "🥈";
export const THIRD_ICON = "🥉";

// time
export const SECOND_AS_MILLISECONDS = 1000;
export const MINUTE_AS_MILLISECONDS = 60 * SECOND_AS_MILLISECONDS;
export const SECOND_IN_MS = 1000;
export const MINUTE_IN_MS = 60 * SECOND_IN_MS;
export const AFK_TIME_IN_MS = 5 * SECOND_IN_MS;
14 changes: 7 additions & 7 deletions src/libs/keystroke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,33 @@ export const SPACE_CHARACTER: string = " ";

export class Keystroke {
private _key: string;
private _timestampInMilliseconds: number;
private _timestampInMs: number;

public constructor(key: string, timestampInMilliseconds: number) {
public constructor(key: string, timestampInMs: number) {
this._key = this.getCorrectKeyLabel(key);
this._timestampInMilliseconds = timestampInMilliseconds;
this._timestampInMs = timestampInMs;
}

public get key(): string {
return this._key;
}

public get timestampInMilliseconds(): number {
return this._timestampInMilliseconds;
public get timestampInMs(): number {
return this._timestampInMs;
}

public toJsonObject(): any {
const jsonObject: any = {
key: this._key,
timestampInMilliseconds: this._timestampInMilliseconds,
timestampInMs: this._timestampInMs,
};

return jsonObject;
}

public static fromJsonObject(jsonObject: any): Keystroke {
const key: string = jsonObject["key"];
const timestamp: number = jsonObject["timestampInMilliseconds"];
const timestamp: number = jsonObject["timestampInMs"];

return new Keystroke(key, timestamp);
}
Expand Down
50 changes: 22 additions & 28 deletions src/libs/keystroke_repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export class KeystrokeRepository {
this._allKeystrokes = keystrokes;
}

public addKeystroke(pressedKey: string, timestampInMilliseconds: number): void {
const keystroke: Keystroke = new Keystroke(pressedKey, timestampInMilliseconds);
public addKeystroke(pressedKey: string, timestampInMs: number): void {
const keystroke: Keystroke = new Keystroke(pressedKey, timestampInMs);
this._allKeystrokes.push(keystroke);
}

Expand All @@ -39,45 +39,45 @@ export class KeystrokeRepository {
}

public yearKeystrokeCount(): number {
const nowInMilliseconds: number = Date.now();
const oneYearAgoInMilliseconds: number = new Date().setFullYear(new Date().getFullYear() - 1);
const nowInMs: number = Date.now();
const oneYearAgoInMs: number = new Date().setFullYear(new Date().getFullYear() - 1);

return this.getKeystrokesInTimeSpan(oneYearAgoInMilliseconds, nowInMilliseconds).length;
return this.getKeystrokesInTimeSpan(oneYearAgoInMs, nowInMs).length;
}

public monthKeystrokeCount(): number {
const nowInMilliseconds: number = Date.now();
const oneMonthAgoInMilliseconds: number = new Date().setMonth(new Date().getMonth() - 1);
const nowInMs: number = Date.now();
const oneMonthAgoInMs: number = new Date().setMonth(new Date().getMonth() - 1);

return this.getKeystrokesInTimeSpan(oneMonthAgoInMilliseconds, nowInMilliseconds).length;
return this.getKeystrokesInTimeSpan(oneMonthAgoInMs, nowInMs).length;
}

public weekKeystrokeCount(): number {
const nowInMilliseconds: number = Date.now();
const oneWeekAgoInMilliseconds: number = new Date().setDate(new Date().getDate() - 7);
const nowInMs: number = Date.now();
const oneWeekAgoInMs: number = new Date().setDate(new Date().getDate() - 7);

return this.getKeystrokesInTimeSpan(oneWeekAgoInMilliseconds, nowInMilliseconds).length;
return this.getKeystrokesInTimeSpan(oneWeekAgoInMs, nowInMs).length;
}

public dayKeystrokeCount(): number {
const nowInMilliseconds: number = Date.now();
const oneDayAgoInMilliseconds: number = new Date().setDate(new Date().getDate() - 1);
const nowInMs: number = Date.now();
const oneDayAgoInMs: number = new Date().setDate(new Date().getDate() - 1);

return this.getKeystrokesInTimeSpan(oneDayAgoInMilliseconds, nowInMilliseconds).length;
return this.getKeystrokesInTimeSpan(oneDayAgoInMs, nowInMs).length;
}

public hourKeystrokeCount(): number {
const nowInMilliseconds: number = Date.now();
const oneHourAgoInMilliseconds: number = new Date().setHours(new Date().getHours() - 1);
const nowInMs: number = Date.now();
const oneHourAgoInMs: number = new Date().setHours(new Date().getHours() - 1);

return this.getKeystrokesInTimeSpan(oneHourAgoInMilliseconds, nowInMilliseconds).length;
return this.getKeystrokesInTimeSpan(oneHourAgoInMs, nowInMs).length;
}

public minuteKeystrokeCount(): number {
const nowInMilliseconds: number = Date.now();
const oneMinuteAgoInMilliseconds: number = new Date().setMinutes(new Date().getMinutes() - 1);
const nowInMs: number = Date.now();
const oneMinuteAgoInMs: number = new Date().setMinutes(new Date().getMinutes() - 1);

return this.getKeystrokesInTimeSpan(oneMinuteAgoInMilliseconds, nowInMilliseconds).length;
return this.getKeystrokesInTimeSpan(oneMinuteAgoInMs, nowInMs).length;
}

public keystrokesToMapWithUniqueKeysInDescendingOrder(): Map<string, number> {
Expand Down Expand Up @@ -123,17 +123,11 @@ export class KeystrokeRepository {
return mapWithUniqueKeys;
}

private getKeystrokesInTimeSpan(
startTimeInMilliseconds: number,
endTimeInMilliseconds: number
): Keystroke[] {
private getKeystrokesInTimeSpan(startTimeInMs: number, endTimeInMs: number): Keystroke[] {
const keystrokesInTimeSpan: Keystroke[] = [];

for (const keystroke of this._allKeystrokes) {
if (
keystroke.timestampInMilliseconds >= startTimeInMilliseconds &&
keystroke.timestampInMilliseconds <= endTimeInMilliseconds
) {
if (keystroke.timestampInMs >= startTimeInMs && keystroke.timestampInMs <= endTimeInMs) {
keystrokesInTimeSpan.push(keystroke);
}
}
Expand Down
93 changes: 56 additions & 37 deletions src/libs/words_per_minute_calculator.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import { MINUTE_AS_MILLISECONDS, SECOND_AS_MILLISECONDS } from "./constants";
import { AFK_TIME_IN_MS, MINUTE_IN_MS } from "./constants";
import { Keystroke } from "./keystroke";
import { KeystrokeRepository } from "./keystroke_repository";

// Calculates the average words per minute
export class WordsPerMinuteCalculator {
private _repository: KeystrokeRepository;
private _fixedTimeSinceLastKeystrokeInMilliseconds: number;
private _fixedTimeSinceLastKeystrokeInMs: number;

constructor(repository: KeystrokeRepository) {
this._repository = repository;
this._fixedTimeSinceLastKeystrokeInMilliseconds = 0;
this._fixedTimeSinceLastKeystrokeInMs = 0;
}

public getAverageWordsPerMinute(): number {
const elapsedMilliseconds: number = this.getAllElapsedTimeInMilliseconds();
if (elapsedMilliseconds === 0) {
const elapsedTimeInMs: number = this.getAllElapsedTimeInMs();
if (elapsedTimeInMs === 0) {
return 0;
}

const elapsedMinutes: number = elapsedMilliseconds / MINUTE_AS_MILLISECONDS;
const elapsedTimeInMin: number = elapsedTimeInMs / MINUTE_IN_MS;
const wpm: number =
this.getWordCountFromKeystrokeCount(this._repository.allKeystrokeCount()) / elapsedMinutes;
this.getWordCountFromKeystrokeCount(this._repository.allKeystrokeCount()) / elapsedTimeInMin;

return wpm;
}
Expand All @@ -30,56 +29,76 @@ export class WordsPerMinuteCalculator {
return keyCount / AVERAGE_WORD_LENGTH;
}

private getAllElapsedTimeInMilliseconds(): number {
private getAllElapsedTimeInMs(): number {
const firstKeystroke: Keystroke = this._repository.getFirstKeystroke();
const lastKeystroke: Keystroke = this._repository.getLastKeystroke();

// invalid
if (!firstKeystroke && !lastKeystroke) {
const noKeystrokes: boolean = this._repository.allKeystrokeCount() === 0;
if (noKeystrokes) {
return 0;
}

// only one
if (firstKeystroke === lastKeystroke) {
const timeInMillisecondsSinceLastKeystroke: number =
Date.now() - lastKeystroke.timestampInMilliseconds;
return timeInMillisecondsSinceLastKeystroke;
const only1Keystroke: boolean = this._repository.allKeystrokeCount() === 1;
if (only1Keystroke) {
return this.getTimeBetweenInMs(Date.now(), lastKeystroke.timestampInMs);
}

// only two
const only2Keystrokes: boolean = this._repository.allKeystrokeCount() === 2;
if (only2Keystrokes) {
return lastKeystroke.timestampInMilliseconds - firstKeystroke.timestampInMilliseconds;
return this.getTimeBetweenInMs(lastKeystroke.timestampInMs, firstKeystroke.timestampInMs);
}

// history
let elapsedMilliseconds: number = 0;
let elapsedTimeInMs: number = this.getElapsedTimeInMsFromPastKeystrokes();
elapsedTimeInMs += this.getElapsedTimeInMsForBetweenActiveAndAFK(lastKeystroke);

return elapsedTimeInMs;
}

private getTimeBetweenInMs(earlierTimeInMs: number, laterTimeInMs: number) {
const timeBetweenInMs: number = Math.abs(laterTimeInMs - earlierTimeInMs);
return timeBetweenInMs;
}

private getElapsedTimeInMsFromPastKeystrokes(): number {
let elapsedTimeInMs: number = 0;

for (let i = 0; i < this._repository.allKeystrokeCount() - 1; i++) {
const currentKeystroke = this._repository.allKeystrokes[i];
const nextKeystroke = this._repository.allKeystrokes[i + 1];
const timeBetweenInMs: number = this.getTimeBetweenInMs(
nextKeystroke.timestampInMs,
currentKeystroke.timestampInMs
);

const timeBetweenInMilliseconds: number =
nextKeystroke.timestampInMilliseconds - currentKeystroke.timestampInMilliseconds;
const isAFK: boolean = timeBetweenInMilliseconds > 5 * SECOND_AS_MILLISECONDS;
if (!isAFK) {
elapsedMilliseconds += timeBetweenInMilliseconds;
if (!this.isAFK(timeBetweenInMs)) {
elapsedTimeInMs += timeBetweenInMs;
continue;
} else {
elapsedMilliseconds += 5 * SECOND_AS_MILLISECONDS;
}

// the time between the keystrokes was afk time and should be added to the elapsed time
elapsedTimeInMs += AFK_TIME_IN_MS;
}

// main loop
const timeSinceLastKeystrokeInMilliseconds: number =
Date.now() - lastKeystroke.timestampInMilliseconds;
const isAFK: boolean = timeSinceLastKeystrokeInMilliseconds > 5 * SECOND_AS_MILLISECONDS;
if (!isAFK) {
this._fixedTimeSinceLastKeystrokeInMilliseconds = timeSinceLastKeystrokeInMilliseconds;
elapsedMilliseconds += timeSinceLastKeystrokeInMilliseconds;
} else {
elapsedMilliseconds += this._fixedTimeSinceLastKeystrokeInMilliseconds;
return elapsedTimeInMs;
}

private getElapsedTimeInMsForBetweenActiveAndAFK(lastKeystroke: Keystroke): number {
const timeSinceLastKeystrokeInMs: number = this.getTimeBetweenInMs(
Date.now(),
lastKeystroke.timestampInMs
);

if (!this.isAFK(timeSinceLastKeystrokeInMs)) {
// the time since the last keystroke was active time and should be added to the elapsed time
this._fixedTimeSinceLastKeystrokeInMs = timeSinceLastKeystrokeInMs;
return timeSinceLastKeystrokeInMs;
}

return elapsedMilliseconds;
// the time since the last keystroke was afk time and should be added to the elapsed time
return this._fixedTimeSinceLastKeystrokeInMs;
}

private isAFK(timeBetweenInMs: number): boolean {
return timeBetweenInMs > AFK_TIME_IN_MS;
}
}
4 changes: 2 additions & 2 deletions src/test/fixtures/correct_fixture.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
[
{
"key": "a",
"timestampInMilliseconds": 0
"timestampInMs": 0
},
{
"key": "b",
"timestampInMilliseconds": 0
"timestampInMs": 0
}
]
}
41 changes: 19 additions & 22 deletions src/test/suite/configuration_loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,25 @@ suite("ConfigurationLoader Test Suite", () => {
});

suite("save()", () => {
test("Writes to file successfully", () => {
configLoader = ConfigurationLoader.getInstance(correctFixtureFilePath);

const expectedJson: any = {
keystrokes: [
{
key: "a",
timestampInMilliseconds: 0,
},
{
key: "b",
timestampInMilliseconds: 0,
},
],
};
const actualJson = TestUtils.readFixture(correctFixtureFilePath);

// todo: problem = the test overwrites the fixture file everytime :(
configLoader.save(expectedJson);

assert.deepStrictEqual(actualJson, expectedJson);
});
// test("Writes to file successfully", () => {
// configLoader = ConfigurationLoader.getInstance(correctFixtureFilePath);
// const expectedJson: any = {
// keystrokes: [
// {
// key: "a",
// timestampInMs: 0,
// },
// {
// key: "b",
// timestampInMs: 0,
// },
// ],
// };
// const actualJson = TestUtils.readFixture(correctFixtureFilePath);
// // todo: problem = the test overwrites the fixture file everytime :(
// configLoader.save(expectedJson);
// assert.deepStrictEqual(actualJson, expectedJson);
// });
});

suite("load()", () => {
Expand Down
Loading

0 comments on commit a567bee

Please sign in to comment.