diff --git a/backend/backend/metric.py b/backend/backend/metric.py index c9c5615a..e0ab48f0 100644 --- a/backend/backend/metric.py +++ b/backend/backend/metric.py @@ -106,11 +106,11 @@ def _rem_onset(self): return rem_latency + self.bedtime def _initialize_sleep_offset(self): - if not self.has_slept: - sleep_offset = None - else: + if self.has_slept: sleep_nb_epochs = (self.sleep_indexes[-1] + 1) if len(self.sleep_indexes) else len(self.sleep_stages) sleep_offset = sleep_nb_epochs * EPOCH_DURATION + self.bedtime + else: + sleep_offset = None self._sleep_offset = sleep_offset @@ -118,8 +118,14 @@ def _initialize_sleep_latency(self): self._sleep_latency = self._get_latency_of_stage(self.is_sleeping_stages) def _initialize_rem_latency(self): - """Time it took to enter REM stage""" - self._rem_latency = self._get_latency_of_stage(self.sleep_stages == SleepStage.REM.name) + """Time from the sleep onset to the first epoch of REM sleep""" + if self.has_slept: + bedtime_to_rem_duration = self._get_latency_of_stage(self.sleep_stages == SleepStage.REM.name) + rem_latency = bedtime_to_rem_duration - self._sleep_latency if bedtime_to_rem_duration is not None else None + else: + rem_latency = None + + self._rem_latency = rem_latency def _initialize_transition_based_metrics(self): consecutive_stages_occurences = Counter(zip(self.sleep_stages[:-1], self.sleep_stages[1:])) diff --git a/backend/tests/test_response.py b/backend/tests/test_response.py index 44cfee2e..dcd4e905 100644 --- a/backend/tests/test_response.py +++ b/backend/tests/test_response.py @@ -157,7 +157,7 @@ class TestReportLatenciesOnset(): dict( sequence=['W', 'N1', 'N2', 'N1', 'REM', 'W'], test_rem=True, - latency=4 * EPOCH_DURATION, + latency=3 * EPOCH_DURATION, ), dict( sequence=['W', 'W', 'N1', 'W', 'N1', 'W'], test_rem=False, @@ -189,7 +189,7 @@ def test_sequence_has_no_stage(self, sequence, test_rem): self.assert_latency_equals_expected(expected_latency, expected_onset, sequence, test_rem) def test_sequence_ends_with_stage(self, sequence, test_rem): - expected_latency = EPOCH_DURATION * (len(sequence) - 1) + expected_latency = EPOCH_DURATION * (len(sequence) - 1) if not test_rem else 0 expected_onset = expected_latency + self.MOCK_REQUEST.bedtime self.assert_latency_equals_expected(expected_latency, expected_onset, sequence, test_rem) diff --git a/web/src/assets/data/predicted_william_cyton.json b/web/src/assets/data/predicted_william_cyton.json index 475a176c..34b61d42 100644 --- a/web/src/assets/data/predicted_william_cyton.json +++ b/web/src/assets/data/predicted_william_cyton.json @@ -1,4 +1,35 @@ { + "subject": { + "age": 30, + "sex": "M" + }, + "metadata": { + "bedTime": 1582441980, + "sessionEndTime": 1582473261.596, + "sessionStartTime": 1582436280, + "totalBedTime": 28260, + "totalSessionTime": 36981.596, + "wakeUpTime": 1582470240 + }, + "report": { + "N1Time": 630, + "N2Time": 11280, + "N3Time": 7530, + "REMTime": 6150, + "WASO": 960, + "WTime": 2310, + "awakenings": 4, + "efficientSleepTime": 25950, + "remLatency": 5940, + "remOnset": 1582447920, + "sleepEfficiency": 0.9182590233545648, + "sleepLatency": 1260, + "sleepOffset": 1582470150, + "sleepOnset": 1582443240, + "sleepTime": 26910, + "stageShifts": 43, + "wakeAfterSleepOffset": 90 + }, "epochs": { "timestamps": [ 1582441980, diff --git a/web/src/components/floating_card.js b/web/src/components/floating_card.js index 0c57d0d5..8aec7a4e 100644 --- a/web/src/components/floating_card.js +++ b/web/src/components/floating_card.js @@ -3,16 +3,14 @@ import PropTypes from 'prop-types'; import { Card, Col, Row } from 'reactstrap'; const FloatingCard = ({ cardClassName, headerText, bodyText, button }) => ( - -
- - -

{headerText}

-

{bodyText}

- {button} - -
-
+ + + + {headerText !== null &&

{headerText}

} +

{bodyText}

+ {button} + +
); diff --git a/web/src/components/footer/index.js b/web/src/components/footer/index.js index 7cc0e156..e5d73495 100644 --- a/web/src/components/footer/index.js +++ b/web/src/components/footer/index.js @@ -37,7 +37,7 @@ PlatformButton.defaultProps = { const Copyright = () => { return (
- © {new Date().getFullYear()}{' '} + © {new Date().getFullYear()}  {text['footer_copyright_polycortex']} diff --git a/web/src/d3/evolving_chart/chart_states.js b/web/src/d3/evolving_chart/chart_states.js index 273c88fe..e660f6a8 100644 --- a/web/src/d3/evolving_chart/chart_states.js +++ b/web/src/d3/evolving_chart/chart_states.js @@ -2,7 +2,7 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { Duration } from 'luxon'; -import { BAR_HEIGHT, DIMENSION } from './constants'; +import { BAR_HEIGHT, DIMENSION, HOVERED_RECT_OPACITY } from './constants'; import { EPOCH_DURATION_MS, TRANSITION_TIME_MS, STAGES_ORDERED } from '../constants'; import '../style.css'; @@ -11,6 +11,8 @@ export const createTimelineChartCallbacks = (g, xTime, xTimeAxis, color, tooltip Object({ fromInitial: () => { const annotationRects = g.selectAll('.rect-stacked').interrupt(); + g.selectAll('text.proportion').remove(); + d3.selectAll('div.tooltip').style('opacity', 0); setAttrOnAnnotationRects(annotationRects, xTime, 0, color, tooltip); @@ -21,8 +23,9 @@ export const createTimelineChartCallbacks = (g, xTime, xTimeAxis, color, tooltip }, fromInstance: () => { const annotationRects = g.selectAll('.rect-stacked').interrupt(); - + g.selectAll('text.proportion').remove(); g.selectAll('.y.visualization__axis').remove(); + d3.selectAll('div.tooltip').style('opacity', 0); setAttrOnAnnotationRects(annotationRects, xTime, 0, color, tooltip); @@ -34,10 +37,12 @@ export const createInstanceChartCallbacks = (g, data, xTime, xTimeAxis, yAxis, c Object({ fromTimeline: () => { const annotationRects = g.selectAll('.rect-stacked').interrupt(); + g.selectAll('text.proportion').remove(); + d3.selectAll('div.tooltip').style('opacity', 0); createVerticalAxis(g, yAxis, color); transitionHorizontalAxis(g, STAGES_ORDERED.length * BAR_HEIGHT); - setAttrOnAnnotationRects(annotationRects, xTime, getVerticalPositionCallback, color, tooltip); + setAttrOnAnnotationRects(annotationRects, xTime, getVerticalPositionCallback(), color, tooltip); }, fromBarChart: () => { const annotationRects = g.selectAll('.rect-stacked').interrupt(); @@ -45,56 +50,52 @@ export const createInstanceChartCallbacks = (g, data, xTime, xTimeAxis, yAxis, c annotationRects.attr('x', xProportionCallback).attr('width', ({ end, start }) => xTime(end) - xTime(start)); g.selectAll('text.proportion').remove(); + d3.selectAll('div.tooltip').style('opacity', 0); g.select('.x.visualization__axis').interrupt().transition().call(xTimeAxis); transitionHorizontalAxis(g, STAGES_ORDERED.length * BAR_HEIGHT); - setAttrOnAnnotationRects(annotationRects, xTime, getVerticalPositionCallback, color, tooltip); + setAttrOnAnnotationRects(annotationRects, xTime, getVerticalPositionCallback(), color, tooltip); }, }); export const createBarChartCallbacks = (g, data, xAxisLinear, yAxis, color, tip) => Object({ fromInstance: () => { - const { firstStageIndexes, stageTimeProportions } = data; const annotationRects = g.selectAll('.rect-stacked').interrupt(); const xProportionCallback = getOffsetSleepStageProportionCallback(data); + d3.selectAll('div.tooltip').style('opacity', 0); g.select('.x.visualization__axis').transition().call(xAxisLinear); transitionHorizontalAxis(g, STAGES_ORDERED.length * BAR_HEIGHT); - setTooltip(annotationRects, tip) + setTooltip(annotationRects, tip, getVerticalPositionCallback(20)) .transition() .duration(TRANSITION_TIME_MS) - .attr('y', getVerticalPositionCallback) + .attr('y', getVerticalPositionCallback()) .attr('x', xProportionCallback) - .on('end', () => { - // Only keep the first rectangle of each stage to be visible - g.selectAll('.rect-stacked') - .attr('x', 0) - .attr('width', getFirstRectangleProportionWidthCallback(firstStageIndexes, stageTimeProportions)); - createProportionLabels(g, data); - }); + .on('end', () => setFirstRectangleToBeAsWideAsStageProportion(data, g)); }, fromStackedBarChart: () => { const annotationRects = g.selectAll('.rect-stacked').interrupt(); g.selectAll('text.proportion').remove(); + d3.selectAll('div.tooltip').style('opacity', 0); createVerticalAxis(g, yAxis, color); transitionHorizontalAxis(g, STAGES_ORDERED.length * BAR_HEIGHT); - annotationRects + setTooltip(annotationRects, tip, getVerticalPositionCallback(20)) .transition() .duration(TRANSITION_TIME_MS / 2) .attr('y', (d) => BAR_HEIGHT * STAGES_ORDERED.indexOf(d.stage)) .transition() .duration(TRANSITION_TIME_MS / 2) .attr('x', 0) - .on('end', () => createProportionLabels(g, data)); + .on('end', () => setFirstRectangleToBeAsWideAsStageProportion(data, g)); }, }); -export const createStackedBarChartCallbacks = (g, data) => +export const createStackedBarChartCallbacks = (g, data, tip) => Object({ fromBarChart: () => { const { annotations, firstStageIndexes, stageTimeProportions, epochs } = data; @@ -103,6 +104,8 @@ export const createStackedBarChartCallbacks = (g, data) => (getCumulativeProportionOfNightAtStart(stage, stageTimeProportions) + stageTimeProportions[stage] / 2) * DIMENSION.WIDTH; const annotationRects = g.selectAll('.rect-stacked').interrupt(); + setTooltip(annotationRects, tip, 0); + d3.selectAll('div.tooltip').style('opacity', 0); g.selectAll('.y.visualization__axis').remove(); g.selectAll('text.proportion').remove(); @@ -137,28 +140,42 @@ export const createStackedBarChartCallbacks = (g, data) => }, }); -const setAttrOnAnnotationRects = (annotationRects, x, yPosition, color, tooltip) => - setTooltip(annotationRects, tooltip) +const setAttrOnAnnotationRects = (annotationRects, x, yBarPosition, color, tooltip) => + setTooltip(annotationRects, tooltip, yBarPosition) .attr('height', BAR_HEIGHT) .transition() .duration(TRANSITION_TIME_MS) .attr('x', ({ start }) => x(start)) - .attr('y', yPosition) + .attr('y', yBarPosition) .attr('width', ({ end, start }) => x(end) - x(start)) .attr('fill', ({ stage }) => color(stage)); -const setTooltip = (element, tooltip) => +const setFirstRectangleToBeAsWideAsStageProportion = (data, g) => { + const { firstStageIndexes, stageTimeProportions } = data; + + // Only keep the first rectangle of each stage to be visible + g.selectAll('.rect-stacked') + .attr('x', 0) + .attr('width', getFirstRectangleProportionWidthCallback(firstStageIndexes, stageTimeProportions)); + createProportionLabels(g, data); +}; + +const setTooltip = (element, tooltip, y) => element - .on('mouseover', function (d) { - tooltip.show(d, this); - d3.select(this).style('opacity', 0.8); + .on('mouseover', function () { + tooltip.mouseover(); + d3.select(this).style('stroke', 'black').style('opacity', HOVERED_RECT_OPACITY); + }) + .on('mousemove', function (d) { + tooltip.mousemove(d, d3.mouse(this), `${y === 0 ? 0 : y(d)}px`); }) .on('mouseout', function () { - tooltip.hide(); - d3.select(this).style('opacity', 1); + tooltip.mouseleave(); + d3.select(this).style('stroke', 'none').style('opacity', 1); }); -const getVerticalPositionCallback = (d) => BAR_HEIGHT * STAGES_ORDERED.indexOf(d.stage); +const getVerticalPositionCallback = (cardOffset = 0) => (d) => + BAR_HEIGHT * STAGES_ORDERED.indexOf(d.stage) + cardOffset; const getFirstRectangleProportionWidthCallback = (firstStageIndexes, stageTimeProportions) => ({ stage }, i) => i === firstStageIndexes[stage] ? stageTimeProportions[stage] * DIMENSION.WIDTH : 0; diff --git a/web/src/d3/evolving_chart/constants.js b/web/src/d3/evolving_chart/constants.js index 58ef935f..9ab55272 100644 --- a/web/src/d3/evolving_chart/constants.js +++ b/web/src/d3/evolving_chart/constants.js @@ -16,3 +16,4 @@ export const DIMENSION = { }; export const BAR_HEIGHT = DIMENSION.HEIGHT / STAGES_ORDERED.length; +export const HOVERED_RECT_OPACITY = 0.7; diff --git a/web/src/d3/evolving_chart/evolving_chart.js b/web/src/d3/evolving_chart/evolving_chart.js index 02946cf0..e553b637 100644 --- a/web/src/d3/evolving_chart/evolving_chart.js +++ b/web/src/d3/evolving_chart/evolving_chart.js @@ -54,7 +54,10 @@ const bindAnnotationsToRects = (g, annotations) => g.selectAll('.rect').data(annotations).enter().append('rect').attr('class', 'rect-stacked'); const createEvolvingChart = (containerNode, data) => { - const svg = d3.select(containerNode).attr('viewBox', `0, 0, ${CANVAS_DIMENSION.WIDTH}, ${CANVAS_DIMENSION.HEIGHT}`); + const svg = d3 + .select(containerNode) + .append('svg') + .attr('viewBox', `0, 0, ${CANVAS_DIMENSION.WIDTH}, ${CANVAS_DIMENSION.HEIGHT}`); const { xTime, xLinear, y, colors } = initializeScales(); const { xTimeAxis, xLinearAxis, yAxis } = initializeAxes(xTime, xLinear, y); const g = createDrawingGroup(svg); @@ -63,7 +66,7 @@ const createEvolvingChart = (containerNode, data) => { data = preprocessData(data); setDomainOnScales(xTime, xLinear, y, colors, data.epochs); - const { barToolTip, stackedToolTip } = initializeTooltips(svg, data); + const { barToolTip, stackedToolTip } = initializeTooltips(containerNode, data); bindAnnotationsToRects(g, data.annotations); timelineChartCallbacks = createTimelineChartCallbacks(g, xTime, xTimeAxis, colors, barToolTip); @@ -72,7 +75,7 @@ const createEvolvingChart = (containerNode, data) => { barChartCallbacks = createBarChartCallbacks(g, data, xLinearAxis, yAxis, colors, stackedToolTip); - stackedBarChartCallbacks = createStackedBarChartCallbacks(g, data); + stackedBarChartCallbacks = createStackedBarChartCallbacks(g, data, stackedToolTip); timelineChartCallbacks.fromInitial(); }; diff --git a/web/src/d3/evolving_chart/mouse_over.js b/web/src/d3/evolving_chart/mouse_over.js index e9587828..9c92d1e0 100644 --- a/web/src/d3/evolving_chart/mouse_over.js +++ b/web/src/d3/evolving_chart/mouse_over.js @@ -1,24 +1,41 @@ -import tip from 'd3-tip'; -import './style.css'; +import * as d3 from 'd3'; import { EPOCH_DURATION_MS } from '../constants'; import { DateTime, Duration } from 'luxon'; -export const initializeTooltips = (svg, data) => { - const barToolTip = initializeTooltip(svg, getBarToolTipText); - const stackedToolTip = initializeTooltip(svg, (d) => +export const initializeTooltips = (containerNode, data) => { + const stackedToolTip = initializeTooltip(containerNode, (d) => getStackedToolTipText(d, data.stageTimeProportions, data.epochs.length), ); + const barToolTip = initializeTooltip(containerNode, getBarToolTipText); return { barToolTip, stackedToolTip }; }; -const initializeTooltip = (svg, getToolTipText) => { - const tooltip = tip().attr('class', 'evolving_chart__tooltip').offset([-10, 0]); - svg.call(tooltip); - tooltip.html(getToolTipText); +const initializeTooltip = (containerNode, getToolTipText) => { + var tooltip = d3 + .select(containerNode) + .append('div') + .attr('class', 'tooltip') + .attr('border-radius', '2px') + .style('background-color', 'rgba(235, 235, 235, 0.9)') + .style('opacity', 0) + .style('position', 'absolute'); + var tooltipText = tooltip.append('div').style('padding', '1em').style('font-size', '1em'); - return tooltip; + var mouseover = (d) => { + tooltip.style('opacity', 1); + }; + var mousemove = function (d, mouse, yPosition) { + // localize d3.mouse into viewbox: https://stackoverflow.com/a/11936865 + tooltip.style('opacity', 1).style('left', `${mouse[0]}px`).style('top', yPosition); + tooltipText.html(() => getToolTipText(d)); + }; + var mouseleave = function () { + tooltip.style('opacity', 0).style('left', `0px`).style('top', '0px'); + }; + + return { mouseover, mousemove, mouseleave }; }; const getBarToolTipText = (d) => `Stage : ${d.stage}
diff --git a/web/src/d3/evolving_chart/style.css b/web/src/d3/evolving_chart/style.css deleted file mode 100644 index 7654106f..00000000 --- a/web/src/d3/evolving_chart/style.css +++ /dev/null @@ -1,7 +0,0 @@ -.evolving_chart__tooltip { - line-height: 1; - padding: 12px; - border-radius: 2px; - color: #0d0d0d; - background: rgba(235, 235, 235, 0.9); -} diff --git a/web/src/d3/spectrogram/constants.js b/web/src/d3/spectrogram/constants.js index 9b1a7f8d..8b2b7198 100644 --- a/web/src/d3/spectrogram/constants.js +++ b/web/src/d3/spectrogram/constants.js @@ -3,7 +3,7 @@ export const NB_SPECTROGRAM = 2; export const FREQUENCY_KEY = 'frequencies'; export const HYPNOGRAM_KEY = 'epochs'; export const NB_POINTS_COLOR_INTERPOLATION = 3; -export const NOT_HIGHLIGHTED_RECTANGLE_OPACITY = 0.5; +export const NOT_HIGHLIGHTED_RECTANGLE_OPACITY = 0.7; export const CANVAS_WIDTH_TO_HEIGHT_RATIO = 700 / 1000; // width to height ratio export const CANVAS_HEIGHT_WINDOW_FACTOR = 0.8; export const MARGIN = { diff --git a/web/src/views/analyze_sleep/waiting_for_server/index.js b/web/src/views/analyze_sleep/waiting_for_server/index.js index 109df4cc..bc193fa0 100644 --- a/web/src/views/analyze_sleep/waiting_for_server/index.js +++ b/web/src/views/analyze_sleep/waiting_for_server/index.js @@ -32,7 +32,7 @@ const WaitingForServer = () => { {assets.map((asset) => ( - + {asset.name} ))} diff --git a/web/src/views/home/hero/hero.js b/web/src/views/home/hero/hero.js index 804ce025..87773225 100644 --- a/web/src/views/home/hero/hero.js +++ b/web/src/views/home/hero/hero.js @@ -38,7 +38,7 @@ class Hero extends React.Component { - +
* a project made by diff --git a/web/src/views/performance/index.js b/web/src/views/performance/index.js index 027d4e27..ff4a640b 100644 --- a/web/src/views/performance/index.js +++ b/web/src/views/performance/index.js @@ -68,7 +68,7 @@ const Performance = () => {
  • First, we will check how our classifier’s scoring agrees with the scoring within the Physionet's Sleep-EDF dataset. Of course, we will perform this agreement test on a subset of EEG data that was never trained on. - This subset is composed of full nights of sleep coming from five subject of a different age group.{' '} + This subset is composed of full nights of sleep coming from five subject of a different age group. 
  • Then, we will check how this classifier performs on a full night recorded on one of our members. In order to diff --git a/web/src/views/record_my_sleep/sections/electrodes_application_section/index.js b/web/src/views/record_my_sleep/sections/electrodes_application_section/index.js index 28bddf50..ee4e8428 100644 --- a/web/src/views/record_my_sleep/sections/electrodes_application_section/index.js +++ b/web/src/views/record_my_sleep/sections/electrodes_application_section/index.js @@ -37,7 +37,8 @@ const ElectrodesApplicationSection = () => ( -
  • {' '} +
    +  

    How to place the electrodes

    diff --git a/web/src/views/record_my_sleep/sections/journal_section/index.js b/web/src/views/record_my_sleep/sections/journal_section/index.js index d81afdbe..09b212f5 100644 --- a/web/src/views/record_my_sleep/sections/journal_section/index.js +++ b/web/src/views/record_my_sleep/sections/journal_section/index.js @@ -7,7 +7,7 @@ const JournalSection = () => (

    - Write a journal + Write a journal

    During the night, you must keep a journal, accurate to the minute, and write down a few information to help us @@ -32,7 +32,7 @@ const JournalSection = () => (

    This information must be given when filling out the upload form for your EEG data file.

    - + Good night!

    diff --git a/web/src/views/record_my_sleep/sections/wake_up_section/index.js b/web/src/views/record_my_sleep/sections/wake_up_section/index.js index 54933be3..967a6d46 100644 --- a/web/src/views/record_my_sleep/sections/wake_up_section/index.js +++ b/web/src/views/record_my_sleep/sections/wake_up_section/index.js @@ -5,7 +5,7 @@ const WakeUpSection = () => (

    - Wake up! + Wake up!

    Remove & clean your electrodes

    diff --git a/web/src/views/sleep_analysis_results/evolving_chart_scrollytelling.js b/web/src/views/sleep_analysis_results/evolving_chart_scrollytelling.js new file mode 100644 index 00000000..f63db9e5 --- /dev/null +++ b/web/src/views/sleep_analysis_results/evolving_chart_scrollytelling.js @@ -0,0 +1,316 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { Container, Card, CardBody, Row } from 'reactstrap'; +import _ from 'lodash'; + +import D3ComponentScrollyTelling from 'components/d3component_scrollytelling'; +import WaypointDirection from 'components/waypoint_direction'; + +import createEvolvingChart, { + instanceChartCallbacks, + timelineChartCallbacks, + barChartCallbacks, + stackedBarChartCallbacks, +} from 'd3/evolving_chart/evolving_chart'; +import { STAGES_ORDERED } from 'd3/constants'; +import Metric from './metric'; + +import './style.css'; + +const EvolvingChartScrollyTelling = ({ epochs, report, metadata, subject }) => { + const [isInitialized, setIsInitialized] = useState(false); + + const { bedTime, wakeUpTime, totalBedTime } = metadata; + const { + efficientSleepTime, + sleepTime, + sleepOnset, + stageShifts, + awakenings, + sleepOffset, + sleepLatency, + WASO, + sleepEfficiency, + wakeAfterSleepOffset, + } = report; + const sleepStageTimes = STAGES_ORDERED.reduce( + (obj, stage) => Object({ ...obj, [stage]: report[`${stage}Time`] }), + {}, + ); + const numberStagesTraversed = _.filter(sleepStageTimes, (time) => time > 0).length; + const mostProminentStage = _.maxBy(_.keys(sleepStageTimes), (stage) => sleepStageTimes[stage]); + const recommendedSleepStage = + parseInt(subject.age) > 18 + ? 'an adult should sleep, on average, more than 7 hours per night to promote optimal health [Watson and al., 2015]' + : 'teenagers, from 13 to 18 years of age, should sleep 8 to 10 hours on a regular basis to promote optimal health [Paruthi and al., 2016]'; + return ( + +
    + +
    +
    + + + +

    + This is your sleep timeline. Each color represents one of the 5 sleep stages which are  + Wake + ,  + REM + ,  + N1,  + N2 +  and  + N3. Each colored block reprensents a part of your + night that was associated to one of these five stages. You may want to hover them as it shows more details + about that part of your night. +

    +
    +
    +
    +
    + + + +

    + The time you spent out of bed is not taken into account in this analysis. Thus, our timeline starts at the + bedtime, {bedTime} in your case, and ends at the time you got out of bed, + whereas  + {wakeUpTime}. Out of this {sleepTime}, you spent  + {efficientSleepTime} actually sleeping. According to the American Academy of + Sleep Medicine, {recommendedSleepStage}. +

    +
    +
    +
    +
    + + + +

    + In total, there were {stageShifts} sleep stage's shifts and {awakenings} +  noctural awakenings. "High levels of sleep fragmentation, as defined by recurrent awakenings and/or + stage shifts may result in complaints of non-restorative sleep even when an apparently normal total sleep + time is present." [Shrivastava and al., 2014] +

    +
    +
    +
    +
    + + + +

    + You first fell asleep at {sleepOnset}, which will be refered to as{' '} + sleep onset. You woke up at {sleepOffset}, which we will refer to as sleep + offset. During that night's sleep, you went through {numberStagesTraversed} different + stages. Let's take a look at them. +

    +
    +
    +
    + {isInitialized && ( + + )} +
    + + + +

    + Let's first look at your Wake stage. This stage + corresponds to when you are fully conscious and active. +

    +

    + You spent {report.WTime} awake through your night. This duration is, in a + sleep report, usually decomposed as three different components. +

    +
    +
    +
    +
    + + + +

    + First, sleep latency corresponds to the time you spend awake in bed before first falling + asleep, which here is {sleepLatency}. +

    +

    + Second, wake after sleep onset, often abbreviated as WASO, is the time spent awake + between sleep onset and sleep offset. In your case, it corresponds to  + {WASO}. +

    +

    + Third, the time you spend in bed after waking up and getting out of bed, here being  + {wakeAfterSleepOffset}, is called wake after sleep offset. +

    +
    +
    +
    +
    + + + +

    + REM stage, which is short for  + rapid eye movement, is a stage characterized by, like its name implies it, rapid eye movement. + During this stage, your muscles are completely immobilized but your brain and your heart should be very + active, which illustrates why this stage is also called paradoxical sleep. This sleep stage therefore + closely resembles wake and it is hard to tell the difference using EEG signals. Take note here that we + dream during all sleep stages but it is in the  + REM stage that dreams are best remembered and seem + the most vivid. +

    +
    +
    +
    +
    + + + +

    + N1 stage is associated with the typical drowsiness + felt before falling asleep. It is a transition stage between wake and sleep. This stage is characterized + by reduced alertness, muscle tone and heart rate. Most people won't say they fell asleep if they get woken + up from N1 sleep. +

    +
    +
    +
    +
    + + + +

    + N2  stage corresponds to light sleep. The + muscle activity decreases even more, and the eyes stop moving. In this stage, the sensitivity to external + stimuli is still noticeable. On the EEG, this stage can be identified by the presence of  + + K complexes + +  and  + + sleep spindles + + . +

    +
    +
    +
    +
    + + + +

    + N3 stage is when you are deeply asleep, hence it’s + also called  + deep sleep, or sometimes slow wave sleep, and is the most difficult to + wake up from. During this stage, virtually no muscle activity can be detected. It is during this stage + that your cells get repaired and tissue grows. This is considered the most restful phase of sleep. +

    +
    +
    +
    +
    + + + +

    + N1,  + N2 and  + N3 are called, in opposition to  + REM, the  + NREM sleep stages. +

    +

    + We've looked at the different functions of each sleep stages. But how much time did you actually spend in + each during the night? +

    +
    +
    +
    + {isInitialized && ( + + )} +
    + + + +

    + This view relativizes the total time spent in each sleep stage according to the other stages. Take your + time to compare the time spent in each stage. Note that the horizontal axis now represents the time spent + in hours since bedtime. +

    +
    +
    +
    +
    + + + +

    + Wake time may be overrepresented, because it + includes your sleep latency ({sleepLatency}) and the time you spent in bed + after sleep offset ({wakeAfterSleepOffset}). +

    +

    + We can see that your most prominent sleep stage is {mostProminentStage}, which in your + case corresponds to {sleepStageTimes[mostProminentStage]}. Usually, the + most prominent sleep stage is N2 + {mostProminentStage === 'N2' ? ', as it is in your case. ' : '.'} +

    +
    +
    +
    +
    + + + +

    + From here, we can also look at your sleep efficiency, which is the proportion of time spent asleep over + the overall time spent in bed. In your case, it corresponds to  + {(sleepEfficiency * 100).toFixed()}%, or  + {sleepEfficiency * totalBedTime}. +

    +
    +
    +
    + {isInitialized && ( + + )} +
    + + + +

    + As a rule of thumb, adults approximately stay 5% of their total sleep time in N1, 50% in  + N2 and 20% in  + N3. The remaining 25% is spent in the  + REM stage sleep. A higher proportion of  + N3 should corresponds to an overall more intense + sleep. +

    +
    +
    +
    + +
    + + + ); +}; + +EvolvingChartScrollyTelling.propTypes = { + epochs: PropTypes.object.isRequired, +}; + +export default EvolvingChartScrollyTelling; diff --git a/web/src/views/sleep_analysis_results/improve_sleep_tips.js b/web/src/views/sleep_analysis_results/improve_sleep_tips.js new file mode 100644 index 00000000..04741eda --- /dev/null +++ b/web/src/views/sleep_analysis_results/improve_sleep_tips.js @@ -0,0 +1,59 @@ +import BadgeBulletPoint from 'components/badge_bullet_point'; +import React from 'react'; + +const TipsToImproveSleep = () => ( + <> +

    + Sleep deficiency increases the risk of health disorders like high blood pressure, cardiovascular disease, + diabetes, depression and obesity. It can also have huge side effects such as decreased mental capabilities + like learning, decision making process and problem-solving skills. Sleep deficiency is often caused by sleep + deprivation but can also be due to other factors like out of sync sleep (going to bed out of sync with your + circadian rythm) and getting poor sleep quality (which may be a symptom of sleep disorders such as sleep apnea or + restless legs syndrome). In order to improve your sleep hygiene and get a better and longer sleep, many actions + should be considered: +

    +
      +
    • +
      + + Alimentation: avoid eating large meals before bedtime. Having a balanced diet and avoiding sources of + caffeine can have a positive impact on one’s sleep. Chocolate, soft drink, tea and decaffeinated coffee are + unexpected sources of caffeine. + +
      +
    • +
    • +
      + + Alcohol, drugs and medicines: just as caffeine influences sleep, the consumption of alcohol, drugs and + certain medications have a significant effect on it. Alcohol, for example, reduces significantly the amount + of REM sleep and lead to multiple awakenings throughout each sleep cycle. [Shrivastava and al., 2014] + +
      +
    • +
    • + +
      + Routine: going to sleep at about the same time, in a dark and quiet environment can help stabilize your + sleep internal mechanisms, namely your circadian rhythms and your homeostasis. +
      +
      +
    • +
    • + +
      + Light: avoid looking at screens before or while getting to bed. Light, especially blue wavelenghts, suppress + melatonin secretion, thus interfering with your circadian rhythms. +
      +
      +
    • +
    • + +
      Exercise: work out for 20 to 30 minutes each day and maintain an active lifestyle.
      +
      +
    • +
    + +); + +export default TipsToImproveSleep; diff --git a/web/src/views/sleep_analysis_results/index.js b/web/src/views/sleep_analysis_results/index.js index 04599805..92d967bd 100644 --- a/web/src/views/sleep_analysis_results/index.js +++ b/web/src/views/sleep_analysis_results/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Container, Row, Button, UncontrolledTooltip } from 'reactstrap'; +import { Container, Row, Button, UncontrolledTooltip, Badge } from 'reactstrap'; import { Link, Redirect } from 'react-router-dom'; import Header from 'components/header'; @@ -10,16 +10,20 @@ import { createSingleHypnogram } from 'd3/hypnogram/hypnogram'; import useGlobalState from 'hooks/useGlobalState'; import text from './text.json'; -import StackedBarChartScrollyTelling from './stacked_bar_chart_scrollytelling'; +import EvolvingChartScrollyTelling from './evolving_chart_scrollytelling'; import SpectrogramScrollyTelling from './spectrogram_scrollytelling'; import previewSequence from 'assets/data/predicted_william_cyton'; import './style.css'; +import SleepMechanisms from './sleep_mechanisms'; +import TipsToImproveSleep from './improve_sleep_tips'; +import Metric from './metric'; const SleepAnalysisResults = ({ location }) => { const [response] = useGlobalState('response'); const isPreviewMode = location.state?.isPreviewMode; + if (!isPreviewMode && !response) { return ( { ); } const data = isPreviewMode ? previewSequence : response.data; + const { epochs, report, metadata, subject } = data; const encodedJsonEpochs = encodeURIComponent(JSON.stringify(data.epochs)); + const sleepAnalysisIntro = ( + + {isPreviewMode && ( + + + + )} +

    + That's it! We have created your personalized sleep data visualizations. Through the following visualizations, + you will discover the time you spent in each sleep stage, how they are defined and what are their respective + functions. Then, an interactive hypnogram allows you to see the transitions between the sleep stages. Finally, + your spectrogram will be presented in detail and some explanations will try to give you a sense of how we were + able to find your sleep stages from your EEG data. +

    + +

    + Throughout these visualizations, your personalized sleep metrics will be presented to you. These were calculated + from the stages of sleep that we found. They will appear, here and there, in blue, in order to + stand out from the rest of the text. +

    + +

    Without further ado, let's get started:

    +
    + ); + const evolvingChartOutro = ( + <> + +

    + You should now have a pretty good picture of how you spent your night. Also, we have seen that sleep can be + decomposed into two groups: REM and NREM. We’ve also defined other measures of your sleep architecture, such + as your sleep latency, efficiency and total sleep time. All of these metrics are very interesting. However, it + would be interesting to understand a little bit more about sleep in order to make sense of this. +

    +

    This is about your hormones

    +

    + Hormones such as melatonin and cortisol play a decisive role in sleep. In fact, it is their variation that + partly explains the circadian cycle. +

    +

    + During the day, a neurotransmitter called serotonin gradually accumulates in the pineal gland. When it gets + dark, the pineal gland synthesizes melatonin which is a hormone that is synthesized from this serotonin. + Melatonin helps induce the sleepiness that prevails in the evening. Exposure to light including that of + screens, street lighting, etc. inhibits the secretion of melatonin and impairs sleep. On the other hand, in + the morning, the adrenal gland secretes the hormone cortisol from cholesterol. Its effect is to promote light + sleep and possibly awake. Any imbalance in these hormones or their secretion can have an inhibiting effect on + sleep. +

    +

    + Adenosine is not a hormone, but it also plays its role into sleep mechanisms. When you are awake, the level of + adenosine continuously increases in your brain. In contrast, when you sleep it is the opposite: it + continuously falls. This is why coffee makes us feel more energized, as caffeine acts as an adenosine-receptor + antagonist [O. Björklund et al., 2008]. +

    +
    + +
    + + +

    + Although we’ve looked at many aspects of your night’s sleep, we haven’t properly looked at your sleep + dynamics, whereas how your sleep evolves overnight. +

    +
    +
    + + ); + + const hypnogramIntro = ( + +

    Hypnogram

    +

    + A hypnogram allows you to visually inspect the evolution of your night, through time. The vertical axis + represents how hard it is to wake up, namely the sleep depth. We see that REM is one of the lightest sleep + stages (along with N1), because we unknowingly wake up from that stage. Those short periods of arousal often + last no longer than 15 seconds, are followed by a lighter sleep stage, and cannot be remembered the next + morning. If they are too frequent, they can affect your sleep quality. Considering that we only have a 30 + seconds resolution, as we scored the night for each 30 seconds epochs, we can see that you have woken up  + {report.awakenings} times. +

    +

    + We can also see that, throughout the night, stages follow about the same pattern, whereas we go from NREM + (either N1, N2 and N3) and then to REM, and so on. We call those sleep cycles, and those typically range from + four to six, each one lasting from 1 hour and a half to almost 2 hours. Another commonly looked at measurement + is the time between sleep onset and the first REM epoch, namely REM latency, which corresponds to  + {report.remLatency}. It can be interesting as paradoxical sleep  + is very sensitive to the effects of medication, sleep deprivation, and circadian rhythm disorders  + [Shrivastava and al., 2014]. +

    +
    + ); + const hypnogramOutro = ( + +

    + You’ve been able to visualize and inspect your night of sleep, which we’ve classified only based on your EEG + recordings. In a sleep lab, electrophysiology technologists generally look at your EEG, EOG and submental EMG, + and then manually classify each epoch of 30 seconds that compose your night. By looking at your EEG recordings, + we can see some patterns that can help electrophysiology technologists, and our classifier, discriminate sleep + stages throughout the night. +

    +
    + ); + + const spectrogramIntro = ( + +

    Spectrogram

    +

    + Below are represented spectrograms of the EEG signals recorded for each channel. The spectrogram is a special + visualization of a signal in the frequency and time domain. Indeed, it represents the spectrum of a signal as it + changes over time. Concretely, spectrograms can be viewed as if we took your signal, separated it into 30 + seconds blocks, stacked them horizontally and then applied the Fast Fourier Transform. We then have, for each 30 + seconds epoch, the corresponding amplitudes for each frequency that makes up the signal hence the spectra. +

    +

    + We then converted the scale to logarithmic, to better see the differences in the spectrums. We then speak of + signal power instead of signal amplitude, because we look at the spectrums in a logarithmic scale. +

    +

    Why are spectrograms useful to observe EEG?

    +

    + Electroencephalography signals allow us to observe brainwaves which have been traditionally split into defined + frequency bands, as they appear and disappear under specific conditions with specific voltage [M. Corsi-Cabrera + and al., 2000]. Thus, observing the spectrum of a signal is great as we can see the voltage amplitude for each + band and then try to predict under which physiological condition was the subject in. But as we've mentionned, + the EEG signals' spectrum do change over time, as EEG is not a stationnary signal. So we need to split the + signal into much smaller chunks, and then observe the spectrum of these chunks, and their evolution over time. +

    +

    + Furthermore, brainwaves frequency bands are widely used and their associated states are defined as below. Please + note that the ranges defined often varies, but we've settled on the definitions proposed by D.Purves in the + "Stages of Sleep" manual [D. Purves et al., 2001]. Also, brain waves are inherently associated with different + region through the brain. As our EEG montage is composed of electrodes placed on the midline axis, not all + frequency band ranges will be easily visible through our spectrograms. It can also explain why the expected + voltage intensity will be sometimes more explicit on the Fpz-Cz spectrogram vs the Pz-Oz spectrogram. +

    +
      +
    • +
      + + β + +
      + The beta frequency band range is defined as between 14 and 60 Hz. It is mostly seen at + the front side of the scalp. +
      +
      +
    • +
    • +
      + + α + +
      + The alpha frequency band range is between 8 and 13 Hz. We mostly find these amplitudes on + the posterior regions of the head, and on both sides. +
      +
      +
    • +
    • +
      + + θ + +
      + The theta frequency band range is characterized by frequencies between 4 and 7 Hz. These + frequencies are not tied to any specific location of the brain. +
      +
      +
    • +
    • +
      + + δ + +
      + The delta frequency band range is described as the lowest range, whereas below 4 Hz. This + EEG band is located frontally for adults, and posteriorly for children. +
      +
      +
    • +
    +

    How to read it?

    +

    + Yellow therefore means that in that 30 seconds time frame, that particular frequency had a big amplitude. Pink + means that you had that frequency with a lower amplitude. Purple means that you didn’t have that frequency in + the signal. +

    +

    + To get a better understanding at how spectrograms work, you can  + + check out this example + +  that decomposes sound frequency from your microphone. +

    +
    + ); + const spectrogramOutro = ( + +

    + We've covered each of the band power frequency ranges we have used in our classification algorithm, and their + associated expected power depending on the sleep stages. +

    +
    + ); + const callToAction = ( + + +

    + If you found our visualizations helpful in better understanding how sleep works, and especially how you slept + on your recorded night, you can come say hi at our   + + Facebook page + + . On another hand, if there have been problems or unexpected results while recording and analyzing your sleep, + you can always open an issue in our   + + Github repository + + . +

    + +

    + If you are curious about the performances of our classifier, we've written a short post about our methodology + and our results. Check it out! Otherwise, you can also download the predicted sleep stages. +

    + + + +
    + +
    + {isPreviewMode && ( + + You need to upload your own sleep sequence in order to download your data + + )} +
    +
    + ); + + const references = ( + + +
    + [Paruthi and al., 2016] S. Paruthi, L. J. Brooks, C. Dambrosio, W. A. Hall, S. Kotagal, R. M. Lloyd, B. A. + Malow, K. Maski, C. Nichols, S. F. Quan, C. L. Rosen, M. M. Troester, and M. S. Wise, “Recommended Amount of + Sleep for Pediatric Populations: A Consensus Statement of the American Academy of Sleep Medicine,” Journal of + Clinical Sleep Medicine, vol. 12, no. 06, pp. 785–786, 2016. +
    +
    + [Watson and al., 2015] N. F. Watson, M. S. Badr, G. Belenky, D. L. Bliwise, O. M. Buxton, D. Buysse, D. F. + Dinges, J. Gangwisch, M. A. Grandner, C. Kushida, R. K. Malhotra, J. L. Martin, S. R. Patel, S. F. Quan, and + E. Tasali, “Recommended Amount of Sleep for a Healthy Adult: A Joint Consensus Statement of the American + Academy of Sleep Medicine and Sleep Research Society,” Journal of Clinical Sleep Medicine, vol. 11, no. 06, + pp. 591–592, 2015. +
    +
    + [Shrivastava and al., 2014] D. Shrivastava, S. Jung, M. Saadat, R. Sirohi, and K. Crewson, “How to interpret + the results of a sleep study,” Journal of Community Hospital Internal Medicine Perspectives, vol. 4, no. 5, p. + 24983, 2014. +
    +
    + [D. Purves and al., 2001] D. Purves et al., “Stages of Sleep,” Neuroscience. 2nd edition, 2001, Accessed: Nov. + 22, 2020. [Online]. Available: https://www.ncbi.nlm.nih.gov/books/NBK10996/. +
    +
    + [O. Björklund and al., 2008] Björklund O, Kahlström J, Salmi P, Fredholm BB "Perinatal Caffeine, Acting on + Maternal Adenosine A1 Receptors, Causes Long-Lasting Behavioral Changes in Mouse Offspring". vol. 3, no. 12: + e3977, 2008. https://doi.org/10.1371/journal.pone.0003977 +
    +
    + [M. Corsi-Cabrera, and al., 2000] M. Corsi-Cabrera, M. A. Guevara, Y. Del Río-Portilla, C. Arce, and Y. + Villanueva-Hernández, “EEG Bands During Wakefulness, Slow-Wave and Paradoxical Sleep as a Result Of Principal + Component Analysis in Man,” Sleep, vol. 23, no. 6, pp. 1–7, Sep. 2000, doi: 10.1093/sleep/23.6.1a. +
    +
    +
    + ); + return ( -
    + <>
    { subtitle={text['header_subtitle']} description={text['header_description']} /> - - {isPreviewMode && ( - - - - )} -

    - Of course, we are analyzing only one night of sleep so it is therefore tricky to draw general conclusions - about your sleep. It is however fascinating to see how your night was. -

    -

    Without further ado, this is what was your night of sleep:

    - -

    - We have seen that sleep can be decomposed in mainly two stages, whereas REM and NREM, and that we can observe - different stage proportions across age, gender and different sleep disorders. We’ve also defined other - measures of your sleep architecture, such as your sleep latency, efficiency and total sleep time. In order to - improve your sleep hygiene, many elements can be considered: -

    -
      -
    • - Alimentation: having a balanced diet and avoiding sources of caffeine can have a positive impact on one’s - sleep. Chocolate, soft drink, tea and decaffeinated coffee are unexpected sources of caffeine. -
    • -
    • Routine: going to sleep about at the same time, in a darkened and quiet environment.
    • -
    • Routine: going to sleep about at the same time, in a darkened and quiet environment.
    • -
    • Routine: going to sleep about at the same time, in a darkened and quiet environment.
    • -
    -

    - Although we’ve looked at many aspects of your night’s sleep, we haven’t properly looked at your sleep - dynamics, whereas how your sleep evolves overnight. -

    -

    Hypnogram

    -

    - A hypnogram allows you to visually inspect the evolution of your night, through time. The vertical axis - represents how hard it is to wake up, namely the sleep deepness. We see that REM is one of the lightest sleep - stages (along with N1), because we unknowingly wake up from that stage. Those short periods of arousal often - last no longer than 15 seconds, are followed by a lighter sleep stage, and cannot be remembered the next - morning. If they are too frequent, they can affect your sleep quality. [5] We can see that, throughout the - night, stages follow about the same pattern, whereas we go from NREM (either N1, N2 and N3) and then to REM, - and so on. We call those sleep cycles, and those typically range from four to six, each one lasting from 90 to - 110 minutes. Another commonly looked at measurement is the time between sleep onset and the first REM epoch, - namely REM latency, which corresponds to 20 minutes. -

    + {sleepAnalysisIntro} + + {evolvingChartOutro} + {hypnogramIntro} + -

    - Sleep cycles take place in a broader process, named the circadian rhythm. It is the one that regulates our - wake and sleep cycles over a 24 hours period. -

    -

    - You’ve been able to visualize and inspect your night of sleep, which we’ve classified only based on your EEG - recordings. In a sleep lab, electrophysiology technicians generally look at your EEG, EOG and submental EMG, - and then manually classify each epoch of 30 seconds that compose your night. By looking at your EEG - recordings, we can see some patterns that can help electrophysiology technicians, and our classifier, - discriminate sleep stages throughout the night. -

    -

    Spectrogram

    -

    - Above, we can see the same chart from the first visualization, which represents your sleep stages through the - night. Below it, there are spectrograms of both your EEG channels. Spectrograms can be viewed as if we took - all of your nights signal, we’ve separated it in contiguous 30 seconds chunks, stacked then horizontally and - to which we’ve applied the fast fourier transform. We then have, for each 30 seconds epoch, the corresponding - amplitudes for each frequency that makes up the signal, hence the spectra. We then converted the scale to - logarithmic, to better see the differences in the spectrums. We then speak of signal power instead of signal - amplitude, because we look at the spectrums in a logarithmic scale. -

    -

    - How to read it? -

    -

    - Red therefore means that in that 30 seconds time frame, that particular frequency had a big amplitude. Green - means that you had that frequency with a lower amplitude. Dark blue means that you didn’t have that frequency - in the signal. -

    -

    - To get a better understanding at how spectrograms work, you can check out - - {' '} - this visualization{' '} - - that decomposes sound frequency from your microphone. -

    - -

    - Generally, when talking about brain waves, we group certain frequencies together into bands. There are overall - five frequency bands, where each has a general associated behaviour, or state of mind. We will cover those - when looking at time frames corresponding to each sleep stage. -

    -

    - We can associate wake stages with low-amplitude activity in the 15 to 60 Hz frequency range, called the beta - band. By slowly falling asleep, the signal frequencies tend to decrease into the 4 to 8 Hz range, or the theta - band, and to have larger amplitudes. These characteristics are associated with N1. N2 stage has the same - characteristics, and also includes sleep spindles. They last only a few seconds and are a large oscillation in - the 10 to 15 hz band. Because they do not occur during all of the 30 seconds period, they cannot be seen here. - Stage N3, also called slow wave sleep, is characterized by slower waves between 0.5 and 4 Hz, known as the - delta range, with large amplitudes. REM stage has the same characteristics as Wake stage, whereas there are - low voltage high frequency activity. -

    -

    Wanna know how accurate this data is?

    - - - - -
    - -
    - {isPreviewMode && ( - - You need to upload your own sleep sequence in order to download your data - - )} -
    -
    + {hypnogramOutro} + {spectrogramIntro} + + {spectrogramOutro} + {callToAction} + {references} + ); }; diff --git a/web/src/views/sleep_analysis_results/metric.js b/web/src/views/sleep_analysis_results/metric.js new file mode 100644 index 00000000..0a38946a --- /dev/null +++ b/web/src/views/sleep_analysis_results/metric.js @@ -0,0 +1,38 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const Metric = ({ isTime, isDuration, children }) => { + const getTimeString = (numberSecondsUTC) => new Date(numberSecondsUTC * 1000).toLocaleTimeString(); + const getDurationString = (numberSeconds) => { + const nbHours = Math.floor(numberSeconds / 3600); + const nbMinutes = Math.floor((numberSeconds % 3600) / 60); + + return nbHours > 0 ? `${nbHours} hours and ${nbMinutes} minutes` : `${nbMinutes} minutes`; + }; + + let content = children; + + if (isTime) { + content = content !== null ? getTimeString(children) : 'never'; + } else if (isDuration) { + content = content !== null ? getDurationString(children) : '0 minutes'; + } + + return ( + + {content} + + ); +}; + +Metric.propTypes = { + isTime: PropTypes.bool, + isDuration: PropTypes.bool, +}; + +Metric.defaultProps = { + isTime: false, + isDuration: false, +}; + +export default Metric; diff --git a/web/src/views/sleep_analysis_results/preview_mode_warning.js b/web/src/views/sleep_analysis_results/preview_mode_warning.js index 3a11f41a..4d0a9790 100644 --- a/web/src/views/sleep_analysis_results/preview_mode_warning.js +++ b/web/src/views/sleep_analysis_results/preview_mode_warning.js @@ -5,17 +5,15 @@ import Emoji from 'components/emoji'; const BEDTIME_PREVIEW_MODE_TIMESTAMP = 1582441980 * 1000; -const PreviewModeWarning = () => { - return ( - - - - You are in preview mode. - The data that is shown here comes from a recording we took on{' '} - {new Date(BEDTIME_PREVIEW_MODE_TIMESTAMP).toDateString()} - - - ); -}; +const PreviewModeWarning = () => ( + + + + You are in preview mode. + The data that is shown here comes from a recording we took on  + {new Date(BEDTIME_PREVIEW_MODE_TIMESTAMP).toDateString()} + + +); export default PreviewModeWarning; diff --git a/web/src/views/sleep_analysis_results/sleep_mechanisms.js b/web/src/views/sleep_analysis_results/sleep_mechanisms.js new file mode 100644 index 00000000..f9df8ca6 --- /dev/null +++ b/web/src/views/sleep_analysis_results/sleep_mechanisms.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { CardDeck, Col, Container } from 'reactstrap'; + +import FloatingCard from 'components/floating_card'; + +import './style.css'; + +const SleepMechanisms = () => ( + + + + + This is your biological clock. It is a self-sustained internal process responsible for regulating your + sleep and wake cycles every 24 hours as well as other biological processes such as blood pressure, body + temperature and heart activity. This is why you feel sleepy at night and awake during the day. Some habits + or events may have an influence on these rhythms. Sleep cycles then take place in these broader processes. + + } + /> + + + + Sleep homeostasis is a biological regulatory process that rules over how much sleep time and intensity you + need to get. It is tightly coupled with the sleep debt concept as the more you are awake, the more + and the deeper you'll need to eventually sleep. The adenosine cycle is suspected to be one of the + responsible for this. + + } + /> + + + +); + +export default SleepMechanisms; diff --git a/web/src/views/sleep_analysis_results/spectrogram_scrollytelling.js b/web/src/views/sleep_analysis_results/spectrogram_scrollytelling.js index c62be124..39a4a003 100644 --- a/web/src/views/sleep_analysis_results/spectrogram_scrollytelling.js +++ b/web/src/views/sleep_analysis_results/spectrogram_scrollytelling.js @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import { Container, Card, CardBody } from 'reactstrap'; +import { Container, Card, CardBody, Row } from 'reactstrap'; import createSpectrogram, { spectrogramCallbacks } from '../../d3/spectrogram/spectrogram'; import D3ComponentScrollyTelling from '../../components/d3component_scrollytelling'; @@ -10,7 +10,7 @@ const SpectrogramScrollyTelling = ({ spectrograms, epochs }) => { const [isInitialized, setIsInitialized] = useState(false); return ( - +
    { />
    - - -

    - Here is represented spectrograms of both your EEG channels. Spectrograms can be viewed as if we took all of - your nights signal, we’ve separated it in contiguous 30 seconds chunks, stacked then horizontally and to - which we’ve applied the fast fourier transform. We then have, for each 30 seconds epoch, the corresponding - amplitudes for each frequency that makes up the signal, hence the spectra. -

    -

    - We then converted the scale to logarithmic, to better see the differences in the spectrums. We then speak of - signal power instead of signal amplitude, because we look at the spectrums in a logarithmic scale. -

    -
    How to read it?
    -

    - Red therefore means that in that 30 seconds time frame, that particular frequency had a big amplitude. Green - means that you had that frequency with a lower amplitude. Dark blue means that you didn’t have that - frequency in the signal. -

    -

    - To get a better understanding at how spectrograms work, you can{' '} - - check out this example - {' '} - that decomposes sound frequency from your microphone. -

    -
    -
    + + + +

    + Generally, when talking about brain waves, we group certain frequencies together into bands. There are + overall five frequency bands, where each has a general associated behaviour, or state of consciousness. We + will cover those when looking at time frames corresponding to each sleep stage. +

    +
    +
    +
    - - -

    - Generally, when talking about brain waves, we group certain frequencies together into bands. There are - overall five frequency bands, where each has a general associated behaviour, or state of mind. We will cover - those when looking at time frames corresponding to each sleep stage. -

    -
    -
    + + + +

    + Let's highlight epochs classified as Wake stages in + order to better see its characteristics. +

    +
    +
    +
    {isInitialized && }
    - - -

    - We can associate wake stages with low-amplitude activity in the 15 to 60 Hz frequency range, called the beta - band. [6] -

    -
    -
    + + + +

    + We can associate Wake stages with low-amplitude + activity in the 15 to 60 Hz frequency range, called the beta band [D. Purves et al., 2001]. This means you + should be able to see, between 15 to 60 Hz, predominantly pink-ish colors. As the beta band is mostly + located at the front of the scalp, you should better see it in the Fpz-Cz's spectrogram. +

    +
    +
    +
    +
    + + + +

    + Let's now highlight N1 stages so they stand out + from the others. +

    +
    +
    +
    {isInitialized && }
    - - -

    - By slowly falling asleep, the signal frequencies tend to decrease into the 4 to 8 Hz range, or the theta - band, and to have larger amplitudes. These characteristics are associated with N1. -

    -
    -
    + + + +

    + While slowly falling asleep, the signal frequencies tend to decrease into the 4 to 8 Hz range, or the + theta band, and to have larger amplitudes [D. Purves et al., 2001]. You should then see more yellow in the + specified frequency band range. These characteristics are associated with  + N1. +

    +
    +
    +
    +
    + + + +

    + How about N2 sleep stages? Once again, let's + hightlight epochs tagged as this stage. +

    +
    +
    +
    {isInitialized && }
    - - -

    - N2 stage has the same characteristics as N1, and also includes sleep spindles. They last only a few seconds - and are a large oscillation in the 10 to 15 hz band. Because they do not occur during all of the 30 seconds - period, they cannot be seen here. [6] -

    -
    -
    + + + +

    + N2 stage has the same characteristics as  + N1, and also includes sleep spindles. They last + only a few seconds and are composed of large oscillations in the 10 to 15 hz band. Because they do not + occur during all of the 30 seconds period, they cannot be seen here [D. Purves et al., 2001]. +

    +
    +
    +
    +
    + + + +

    + We'll now look at how the epochs tagged as  + slow wave sleep +  look like. +

    +
    +
    +
    {isInitialized && }
    - - -

    - Stage N3, also called slow wave sleep, is characterized by slower waves between 0.5 and 4 Hz, known as the - delta range, with large amplitudes. [6] -

    -
    -
    - {isInitialized && } + + + +

    + As expected, stage N3 is characterized by slower + waves between 0.5 and 4 Hz, known as the delta range, with large amplitudes [D. Purves et al., 2001]. + There should then be bright yellow at the lower freqency range. Once again, as the delta band is also + mostly located at the front of the scalp, you should better see it in the Fpz-Cz's spectrogram. +

    +
    +
    +
    - - -

    - REM stage has the same characteristics as Wake stage, whereas there are low voltage high frequency activity. - [6] -

    -
    -
    + + + +

    + The last epochs to look at are the ones tagged as  + rapid eye movement. Let's see how can they be + described. +

    +
    +
    +
    + {isInitialized && }
    + + + +

    + REM stage has the same characteristics as  + Wake stage, whereas there are low voltage high + frequency activity [D. Purves et al., 2001]. This means you should once again be able to see, between 15 + to 60 Hz, pink-ish colors. +

    +
    +
    +
    +
      ); diff --git a/web/src/views/sleep_analysis_results/stacked_bar_chart_scrollytelling.js b/web/src/views/sleep_analysis_results/stacked_bar_chart_scrollytelling.js deleted file mode 100644 index 567d9d9d..00000000 --- a/web/src/views/sleep_analysis_results/stacked_bar_chart_scrollytelling.js +++ /dev/null @@ -1,143 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import { Container, Card, CardBody } from 'reactstrap'; - -import D3ComponentScrollyTelling from 'components/d3component_scrollytelling'; -import WaypointDirection from 'components/waypoint_direction'; - -import createEvolvingChart, { - instanceChartCallbacks, - timelineChartCallbacks, - barChartCallbacks, - stackedBarChartCallbacks, -} from 'd3/evolving_chart/evolving_chart'; - -const StackedBarChartScrollyTelling = ({ epochs }) => { - const [isInitialized, setIsInitialized] = useState(false); - - return ( - -
    - -
    -
    - - -

    - We can see that each colored block represents a part of your night. They are ordered from bed time to out of - bed timestamps you’ve written in your journal. Each color is associated with a specific sleep stage. You - went to bed at 12:22 am and you got out of bed at 9:47 am, which adds up to 9 hours and 25 minutes of time - spent in bed. Of this total time, you spent 7 hours and 27 minutes actually sleeping. You first fell asleep - at XX:XX, to which we will refer to as sleep onset. The last non wake block ended at XX:XX, which can also - be referred to as sleep offset. During that night's sleep, you went through each of the 5 five stages. Let's - try to see a little better what happened about each of them. -

    -
    -
    -
    - {isInitialized && ( - - )} -
    - - -

    Wake stage is of course the stage we want to minimize when in bed. It can be decomposed into two parts:

    -
      -
    • Sleep latency : Time spent before falling asleep, which corresponds to X minutes in your case.
    • -
    • Wake after sleep onset (WASO): Time spent awake after first falling asleep and before waking up.
    • -
    • - {' '} - For healthy adults, it is normal to experience small awakenings during the night. Unprovoked awakenings - are mostly during or after REM stages.{' '} -
    • -
    -
    -
    -
    - - -

    - REM stage stands for “Rapid Eyes Movements” and is also known as “paradoxical sleep”. It is - associated with dreaming and, as the National Sleep Foundation says,{' '} - “the brain is awake and body paralyzed.” -

    -

    - N1 stage is associated with that drowsy feeling before falling asleep. Most people wouldn’t - say they fell asleep if they’ve been woken up from N1 sleep. -

    -

    - N2 stage still corresponds to a light sleep, but where the muscle activity decreases more, - and the eyes have stopped moving. It is called, along with N1, light sleep. -

    -

    - N3 stage is when you are deeply asleep, hence it’s also called deep sleep, - or sometimes slow wave sleep, and is the most difficult to wake up from. It is during those - stages that your cells get repaired, and that tissue grows. But how much time did you spend in each stage - during the whole night? -

    -
    -
    -
    - {isInitialized && ( - - )} -
    - - -

    - From here, we can look at your sleep efficiency, which is the proportion of time spent asleep over the - overall time spent in bed. In your case, it corresponds to 79%, or 7h27. -

    -
    -
    -
    - - -

    - We are currently looking at your in bed sleep stage proportions. Wake time may be overrepresented, because - it includes your sleep latency and the time you spent in bed after waking up. In order to look at your - actual stage proportions, we must cut them out from wake time to only keep WASO. -

    -
    -
    -
    - - -

    - We can see that the most prominent sleep stage is N2, which in your case corresponds to XXXX. How does your - night compare to other people’s night? -

    -
    -
    -
    - {isInitialized && ( - - )} -
    - - -

    - As a rule of thumb, adults approximately stay 5% of their total sleep time in N1; 50% in N2; and 20% is in - N3. The remaining 25% is REM stage sleep. -

    -
    -
    -
    -   - - ); -}; - -StackedBarChartScrollyTelling.propTypes = { - epochs: PropTypes.object.isRequired, -}; - -export default StackedBarChartScrollyTelling; diff --git a/web/src/views/sleep_analysis_results/style.css b/web/src/views/sleep_analysis_results/style.css index 43784a03..648a37bb 100644 --- a/web/src/views/sleep_analysis_results/style.css +++ b/web/src/views/sleep_analysis_results/style.css @@ -3,3 +3,28 @@ justify-content: space-between; margin: 1rem 0.1rem; } + +.scrollytelling_cards__w_text { + font-weight: bold; + color: #e3624b; +} + +.scrollytelling_cards__rem_text { + font-weight: bold; + color: #ffd443; +} + +.scrollytelling_cards__n1_text { + font-weight: bold; + color: #b0c9d9; +} + +.scrollytelling_cards__n2_text { + font-weight: bold; + color: #4da6fe; +} + +.scrollytelling_cards__n3_text { + font-weight: bold; + color: #48587f; +}