Last active
April 19, 2021 14:10
-
-
Save alexFaunt/512010851d992c00caba055e951f3272 to your computer and use it in GitHub Desktop.
Diff
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/README.md b/README.md | |
index 2b34e2a..2a0b245 100644 | |
--- a/README.md | |
+++ b/README.md | |
@@ -3,14 +3,13 @@ | |
## Setup | |
``` | |
-yarn && (cd client && yarn) | |
+yarn | |
``` | |
- | |
## Running | |
``` | |
-yarn dev | |
+yarn server | |
``` | |
## Tests | |
@@ -22,27 +21,15 @@ yarn test | |
## Background | |
At Fiit we collect stats for user workouts, we then present these stats to our users in meaningful ways. | |
-The aim of this task is to present a styled list of workouts with associated stats. | |
+The aim of this task is to fetch data stored for workouts and do some aggregate calculations on it. | |
## Tasks | |
-### Full stack | |
- | |
-1. Make the `workouts.js` api return results from the workout.json file | |
-2. Display drop down list of workouts returned in the UI instead of always first workout | |
-3. Hook up stats api with workoutId to fetch stats for the workout | |
-4. Implement stats api to calculate average heart rate and total reps from the json data | |
-5. If the stats fail to load in under 2 seconds, return an error instead (workout #5 looks particularly slow) | |
-6. Style the results page to make it look nicer | |
- | |
-### Frontend | |
- | |
-1. Display drop down list of workouts returned in the UI instead of always first workout | |
-2. Add and extend the existing Stats component to the page. When a workout is selected from the drop down list: load the stats for that workout and display them. Some hard coded stats are returned from GraphQL which will suffice for this step. | |
-3. Show the image for the selected workout so that it is centered in a square on all screen sizes - design.png | |
-4. Hook up the rest of the workouts and randomise the stats in GraphQL - if comfortable attempt to load the json data from the files. | |
-5. On changing the selected workout, add some kind of animation or fade transition between the old details and new ones | |
-6. Talk through breaking up the files / managing this state appropriately in production | |
+1. Complete the function `loadStatsFilesById` in `server/src/api/data/utils/load-stats-files.js` (ignore the delay part). You'll probably want to write a unit test to make sure it works | |
+2. Using this function, update `getStats` in `server/src/api/stats.js` to calculate and return the total reps and then the average heart rate for a given workout. Tests already exist for this | |
+3. If the stats fail to load in under 2 seconds, return an error instead - comment back in `delayedLoadStatsFilesById` in `load-stats-files` to make workout 5 load slowly | |
+4. Extend the tests for task number 3 | |
+5. GraphQL extension - add stats to the type Workout and resolve it using `getStats` - playground can be used to test | |
## Followup questions | |
diff --git a/package.json b/package.json | |
index f04c29a..0b90813 100644 | |
--- a/package.json | |
+++ b/package.json | |
@@ -4,8 +4,7 @@ | |
"scripts": { | |
"client": "cd client && yarn start", | |
"server": "nodemon --ignore ./client server/src/server.js", | |
- "dev": "REACT_APP_SERVER_PORT=$(./scripts/get-available-port.sh) concurrently --kill-others-on-fail \"yarn server\" \"yarn client\"", | |
- "test": "jest server/ && (cd client && yarn test)" | |
+ "test": "jest" | |
}, | |
"dependencies": { | |
"apollo-server-express": "^2.11.0", | |
diff --git a/server/src/api/data/utils/delay.js b/server/src/api/data/utils/delay.js | |
new file mode 100644 | |
index 0000000..254565d | |
--- /dev/null | |
+++ b/server/src/api/data/utils/delay.js | |
@@ -0,0 +1,3 @@ | |
+const delay = (time) => new Promise((resolve) => setTimeout(resolve, time)); | |
+ | |
+module.exports = delay; | |
diff --git a/server/src/api/data/utils/load-stats-files.js b/server/src/api/data/utils/load-stats-files.js | |
index f77c169..0c64698 100644 | |
--- a/server/src/api/data/utils/load-stats-files.js | |
+++ b/server/src/api/data/utils/load-stats-files.js | |
@@ -1,26 +1,23 @@ | |
-const fs = require('fs'); | |
-const path = require('path'); | |
-const { promisify } = require('util'); | |
+const delay = require('./delay'); | |
-const readDir = promisify(fs.readdir); | |
-const readFile = promisify(fs.readFile); | |
+async function loadStatsFilesById(id) { | |
+ // Given workout id number 1, load the appropriate stats files | |
+ return statsFiles; | |
+}; | |
+ | |
+module.exports = loadStatsFilesById | |
-const delay = (time) => new Promise((resolve) => setTimeout(resolve, time)); | |
-async function loadStatsFilesById(id) { | |
- const folder = path.resolve(__dirname, `../stats/workout${id}`); | |
- const fileNames = await readDir(folder); | |
- const fileReads = fileNames.map((fileName) => readFile(path.join(folder, fileName), 'utf-8')); | |
- const statsFiles = await Promise.all(fileReads); | |
- // simulate bad network | |
+ | |
+// For a later task we'll simulate a network delay on workout number 5 - ignore for now! | |
+async function delayedLoadStatsFilesById(id) { | |
if (id === 5) { | |
await delay(5000); | |
} | |
+ return loadStatsFilesById(id); | |
+} | |
- return statsFiles; | |
-}; | |
- | |
-module.exports = loadStatsFilesById | |
+// module.exports = delayedLoadStatsFilesById | |
diff --git a/server/src/api/data/utils/load-stats-files.test.js b/server/src/api/data/utils/load-stats-files.test.js | |
new file mode 100644 | |
index 0000000..341ea2a | |
--- /dev/null | |
+++ b/server/src/api/data/utils/load-stats-files.test.js | |
@@ -0,0 +1,12 @@ | |
+const loadStatsFilesById = require('./load-stats-files'); | |
+ | |
+ | |
+it('It loads the stats files by workout id', async () => { | |
+ const results = await loadStatsFilesById(1); | |
+ | |
+ // The first workout has 158 chunks | |
+ | |
+ // And at the start of the 30th chunk my heart rate was 156 | |
+ | |
+ expect(results).toBe('hmmmm? what could i check?') | |
+}); | |
diff --git a/server/src/api/stats.js b/server/src/api/stats.js | |
index 5236ab4..4528f2d 100644 | |
--- a/server/src/api/stats.js | |
+++ b/server/src/api/stats.js | |
@@ -1,12 +1,12 @@ | |
const loadStatsFilesById = require('./data/utils/load-stats-files'); | |
-async function getStats() { | |
+async function getStats(workoutId) { | |
return { | |
- bpm: { | |
- average: 113, | |
- }, | |
reps: { | |
- sum: 225 | |
+ sum: 222, | |
+ }, | |
+ bpm: { | |
+ average: 141, | |
}, | |
}; | |
} | |
diff --git a/server/src/api/stats.test.js b/server/src/api/stats.test.js | |
index 3fafe41..1ee1457 100644 | |
--- a/server/src/api/stats.test.js | |
+++ b/server/src/api/stats.test.js | |
@@ -1,11 +1,45 @@ | |
const stats = require('./stats'); | |
-it('I can fetch stats of workout 1', async () => { | |
- const results = await stats(1); | |
+it('returns the sum of reps for each workout', async () => { | |
+ const results1 = await stats(1); | |
+ expect(results1.reps.sum).toBe(0); | |
- expect(results).toMatchObject({ | |
- averageHeartRate: '113', | |
- totalReps: '255' | |
- }); | |
+ const results2 = await stats(2); | |
+ expect(results2.reps.sum).toBe(222); | |
+ const results3 = await stats(3); | |
+ expect(results3.reps.sum).toBe(0); | |
+ | |
+ const results4 = await stats(4); | |
+ expect(results4.reps.sum).toBe(169); | |
+}); | |
+ | |
+// Depending on the calculation method you can get different results, we're not too fussed what method is used | |
+it('returns the average bpm for each workout', async () => { | |
+ const results1 = await stats(1); | |
+ expect(results1.bpm.average).toBe(139); | |
+ | |
+ const results2 = await stats(2); | |
+ expect(results2.bpm.average).toBe(141); | |
+ | |
+ const results3 = await stats(3); | |
+ expect(results3.bpm.average).toBe(135); | |
+ | |
+ const results4 = await stats(4); | |
+ expect(results4.bpm.average).toBe(136); | |
+}); | |
+ | |
+// Depending on the calculation method you can get different results, we're not too fussed what method is used | |
+it.skip('returns the average bpm for each workout', async () => { | |
+ const results1 = await stats(1); | |
+ expect(results1.bpm.average).toBe(137); | |
+ | |
+ const results2 = await stats(2); | |
+ expect(results2.bpm.average).toBe(140); | |
+ | |
+ const results3 = await stats(3); | |
+ expect(results3.bpm.average).toBe(134); | |
+ | |
+ const results4 = await stats(4); | |
+ expect(results4.bpm.average).toBe(135); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment