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

Use UTC for daily stats calculations #2359

Merged
merged 9 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from 8 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
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { storiesOf } from '@storybook/react'
import zooTheme from '@zooniverse/grommet-theme'
import { Grommet } from 'grommet'

import DailyClassificationsChart from './DailyClassificationsChartContainer'
import DailyClassificationsChartContainer from './DailyClassificationsChartContainer'
const MOCK_DAILY_COUNTS = [
{ count: 87, period: '2019-09-30' },
{ count: 32, period: '2019-10-01' },
Expand All @@ -17,13 +16,25 @@ const MOCK_TOTALS = {
today: 25
}

storiesOf('Project App / Screens / Classify / Daily Classifications Chart', module)
.add('plain', () => (
export default {
title: 'Project App / Screens / Classify / Daily Classifications Chart',
component: DailyClassificationsChartContainer,
args: {
counts: MOCK_TOTALS,
projectName: 'Snapshot Serengeti',
thisWeek: MOCK_DAILY_COUNTS
}
}

export function Plain({ counts, projectName, thisWeek }) {
return (
<Grommet theme={zooTheme}>
<DailyClassificationsChart.wrappedComponent
counts={MOCK_TOTALS}
thisWeek={MOCK_DAILY_COUNTS}
projectName='Snapshot Serengeti'
<DailyClassificationsChartContainer
counts={counts}
thisWeek={thisWeek}
projectName={projectName}
/>
</Grommet>
))
)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { MobXProviderContext, observer } from 'mobx-react'
import { useContext } from 'react'

import DailyClassificationsChartContainer from './DailyClassificationsChartContainer'

function storeMapper(store) {
const {
project,
user: {
personalization: {
counts,
stats: {
thisWeek
}
}
}
} = store

return {
counts,
thisWeek,
projectName: project['display_name']
}
}

function DailyClassificationsChartConnector() {
const { store } = useContext(MobXProviderContext)
const { counts, thisWeek, projectName } = storeMapper(store)
return (
<DailyClassificationsChartContainer
counts={counts}
projectName={projectName}
thisWeek={thisWeek}
/>
)
}

export default observer(DailyClassificationsChartConnector)
Original file line number Diff line number Diff line change
@@ -1,56 +1,45 @@
import counterpart from 'counterpart'
import { inject, observer } from 'mobx-react'
import { array, number, shape, string } from 'prop-types'
import { Component } from 'react'

import DailyClassificationsChart from './DailyClassificationsChart'

function storeMapper (stores) {
const { project, user: { personalization: { counts, stats: { thisWeek } } } } = stores.store
return {
counts,
thisWeek,
projectName: project['display_name']
}
const defaultCounts = {
today: 0
}

@inject(storeMapper)
@observer
class DailyClassificationsChartContainer extends Component {
render () {
const TODAY = new Date()
const { counts, projectName, thisWeek } = this.props
const stats = thisWeek.map(stat => {
const day = new Date(stat.period)
const locale = counterpart.getLocale()
const count = (day.getDay() === TODAY.getDay()) ? counts.today : stat.count
const longLabel = day.toLocaleDateString(locale, { weekday: 'long' })
const alt = `${longLabel}: ${count}`
const label = day.toLocaleDateString(locale, { weekday: 'narrow' })
return Object.assign({}, stat, { alt, count, label, longLabel })
})
return (
<DailyClassificationsChart
stats={stats}
projectName={projectName}
/>
)
}
function DailyClassificationsChartContainer({
counts = defaultCounts,
projectName,
thisWeek = []
}) {
const TODAY = new Date()
const locale = counterpart.getLocale()
const stats = thisWeek.map(({ count: statsCount, period }) => {
const day = new Date(period)
const isToday = day.getUTCDay() === TODAY.getDay()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could put the day numbers and local names for each day in the store, so that each daily count is { count, dayNumber, label, longLabel, period }. They never change, so only need to be generated once. Maybe something for a future rewrite of this component?

const count = isToday ? counts.today : statsCount
const longLabel = day.toLocaleDateString(locale, { timeZone: 'UTC', weekday: 'long' })
const alt = `${longLabel}: ${count}`
const label = day.toLocaleDateString(locale, { timeZone: 'UTC', weekday: 'narrow' })
return { alt, count, label, longLabel, period }
})
return (
<DailyClassificationsChart
stats={stats}
projectName={projectName}
/>
)
}

DailyClassificationsChartContainer.propTypes = {
/** Today's counts, updated as classifications are made. */
counts: shape({
today: number
}),
/** Project name */
projectName: string.isRequired,
/** Array of daily stats from the stats server */
thisWeek: array
}

DailyClassificationsChartContainer.defaultProps = {
counts: {
today: 0
},
thisWeek: []
}

export default DailyClassificationsChartContainer
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,62 @@ describe('Component > DailyClassificationsChartContainer', function () {
const MOCK_TOTALS = {
today: 25
}
const CHART_DATA = [
{
alt: 'Monday: 12',
count: 12,
period: '2019-09-30',
label: 'M',
longLabel: 'Monday'
},
{
alt: 'Tuesday: 13',
count: 13,
period: '2019-10-01',
label: 'T',
longLabel: 'Tuesday'
},
{
alt: 'Wednesday: 14',
count: 14,
period: '2019-10-02',
label: 'W',
longLabel: 'Wednesday'
},
{
alt: 'Thursday: 10',
count: 10,
period: '2019-10-03',
label: 'T',
longLabel: 'Thursday'
},
{
alt: 'Friday: 11',
count: 11,
period: '2019-10-04',
label: 'F',
longLabel: 'Friday'
},
{
alt: 'Saturday: 8',
count: 8,
period: '2019-10-05',
label: 'S',
longLabel: 'Saturday'
},
{
alt: 'Sunday: 25',
count: 25,
period: '2019-10-06',
label: 'S',
longLabel: 'Sunday'
}
]

before(function () {
clock = sinon.useFakeTimers({ now: new Date('2019-10-06'), toFake: ['Date'] })
clock = sinon.useFakeTimers({ now: new Date('2019-10-06T12:00:00Z'), toFake: ['Date'] })
wrapper = shallow(
<DailyClassificationsChartContainer.wrappedComponent
<DailyClassificationsChartContainer
counts={MOCK_TOTALS}
projectName='Test Project'
thisWeek={MOCK_DAILY_COUNTS}
Expand All @@ -47,18 +98,6 @@ describe('Component > DailyClassificationsChartContainer', function () {

describe('daily stats counts', function () {
let stats
function mockStat (count, period) {
const day = new Date(period)
const dayNameShort = day.toLocaleDateString('en', { weekday: 'narrow' })
const dayNameFull = day.toLocaleDateString('en', { weekday: 'long' })
return {
count,
period,
alt: `${dayNameFull}: ${count}`,
label: dayNameShort,
longLabel: dayNameFull
}
}

before(function () {
stats = componentWrapper.prop('stats')
Expand All @@ -74,9 +113,7 @@ describe('Component > DailyClassificationsChartContainer', function () {

before(function () {
stat = stats[day]
expectedStat = stat.period === '2019-10-06'
? mockStat(MOCK_TOTALS.today, stat.period)
: mockStat(stat.count, stat.period)
expectedStat = CHART_DATA[day]
})

describe(MOCK_DAILY_COUNTS[day].period, function () {
Expand All @@ -92,7 +129,7 @@ describe('Component > DailyClassificationsChartContainer', function () {
expect(stat.label).to.equal(expectedStat.label)
})

it('should have a long label', function () {
it(`should be ${CHART_DATA[day].longLabel}`, function () {
expect(stat.longLabel).to.equal(expectedStat.longLabel)
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default } from './DailyClassificationsChartContainer'
export { default } from './DailyClassificationsChartConnector'
8 changes: 4 additions & 4 deletions packages/app-project/stores/YourStats.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ export const statsClient = new GraphQLClient('https://graphql-stats.zooniverse.o

// https://stackoverflow.com/a/51918448/10951669
function firstDayOfWeek (dateObject, firstDayOfWeekIndex) {
const dayOfWeek = dateObject.getDay()
const dayOfWeek = dateObject.getUTCDay()
const firstDayOfWeek = new Date(dateObject)
const diff = dayOfWeek >= firstDayOfWeekIndex
? dayOfWeek - firstDayOfWeekIndex
: 6 - dayOfWeek

firstDayOfWeek.setDate(dateObject.getDate() - diff)
firstDayOfWeek.setUTCDate(dateObject.getUTCDate() - diff)

return firstDayOfWeek
}
Expand Down Expand Up @@ -42,8 +42,8 @@ const YourStats = types
const monday = firstDayOfWeek(today, 1) // Monday is day number 1 in JavaScript
for (let day = 0; day < 7; day++) {
const weekDay = new Date(monday.toISOString())
const newDate = monday.getDate() + day
weekDay.setDate(newDate)
const newDate = monday.getUTCDate() + day
weekDay.setUTCDate(newDate)
const period = weekDay.toISOString().substring(0, 10)
const { count } = dailyCounts.find(count => count.period.startsWith(period)) || { count: 0, period }
weeklyStats.push({
Expand Down