From 5de1fe4e9459dde99593deb878cbe66f4eab11e7 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 4 Jan 2019 10:07:24 +0100 Subject: [PATCH] refactor --- fittrackee_client/src/actions/activities.js | 2 +- .../ActivityDisplay/ActivityCardHeader.jsx | 3 +- .../Activity/ActivityDisplay/ActivityMap.jsx | 3 +- .../Activity/ActivityForms/FormWithoutGpx.jsx | 2 +- .../components/Common/Stats/CustomTooltip.jsx | 2 +- .../components/Common/Stats/StatsChart.jsx | 3 +- .../src/components/Common/Stats/index.jsx | 2 +- .../src/components/Dashboard/Calendar.jsx | 3 +- .../src/components/Dashboard/Records.jsx | 2 +- fittrackee_client/src/utils.js | 226 ------------------ fittrackee_client/src/utils/activities.js | 74 ++++++ fittrackee_client/src/utils/index.js | 55 +++++ fittrackee_client/src/utils/stats.js | 108 +++++++++ 13 files changed, 250 insertions(+), 235 deletions(-) delete mode 100644 fittrackee_client/src/utils.js create mode 100644 fittrackee_client/src/utils/activities.js create mode 100644 fittrackee_client/src/utils/index.js create mode 100644 fittrackee_client/src/utils/stats.js diff --git a/fittrackee_client/src/actions/activities.js b/fittrackee_client/src/actions/activities.js index a166891b..23dffdb1 100644 --- a/fittrackee_client/src/actions/activities.js +++ b/fittrackee_client/src/actions/activities.js @@ -2,7 +2,7 @@ import { parse } from 'date-fns' import FitTrackeeGenericApi from '../fitTrackeeApi' import { history } from '../index' -import { formatChartData } from '../utils' +import { formatChartData } from '../utils/stats' import { setError, setLoading } from './index' import { loadProfile } from './user' diff --git a/fittrackee_client/src/components/Activity/ActivityDisplay/ActivityCardHeader.jsx b/fittrackee_client/src/components/Activity/ActivityDisplay/ActivityCardHeader.jsx index 915f1591..33217d69 100644 --- a/fittrackee_client/src/components/Activity/ActivityDisplay/ActivityCardHeader.jsx +++ b/fittrackee_client/src/components/Activity/ActivityDisplay/ActivityCardHeader.jsx @@ -1,7 +1,8 @@ import React from 'react' import { Link } from 'react-router-dom' -import { formatActivityDate, getDateWithTZ } from '../../../utils' +import { getDateWithTZ } from '../../../utils' +import { formatActivityDate } from '../../../utils/activities' export default function ActivityCardHeader(props) { diff --git a/fittrackee_client/src/components/Activity/ActivityDisplay/ActivityMap.jsx b/fittrackee_client/src/components/Activity/ActivityDisplay/ActivityMap.jsx index 6a423541..a27da93a 100644 --- a/fittrackee_client/src/components/Activity/ActivityDisplay/ActivityMap.jsx +++ b/fittrackee_client/src/components/Activity/ActivityDisplay/ActivityMap.jsx @@ -4,7 +4,8 @@ import { GeoJSON, Map, TileLayer } from 'react-leaflet' import { connect } from 'react-redux' import { getActivityGpx } from '../../../actions/activities' -import { getGeoJson, thunderforestApiKey } from '../../../utils' +import { thunderforestApiKey } from '../../../utils' +import { getGeoJson } from '../../../utils/activities' class ActivityMap extends React.Component { diff --git a/fittrackee_client/src/components/Activity/ActivityForms/FormWithoutGpx.jsx b/fittrackee_client/src/components/Activity/ActivityForms/FormWithoutGpx.jsx index 4d42c397..d9c0be4b 100644 --- a/fittrackee_client/src/components/Activity/ActivityForms/FormWithoutGpx.jsx +++ b/fittrackee_client/src/components/Activity/ActivityForms/FormWithoutGpx.jsx @@ -5,7 +5,7 @@ import { addActivityWithoutGpx, editActivity } from '../../../actions/activities' import { history } from '../../../index' -import { formatActivityDate } from '../../../utils' +import { formatActivityDate } from '../../../utils/activities' function FormWithoutGpx (props) { const { activity, onAddOrEdit, sports } = props diff --git a/fittrackee_client/src/components/Common/Stats/CustomTooltip.jsx b/fittrackee_client/src/components/Common/Stats/CustomTooltip.jsx index ff5c3916..7b52a810 100644 --- a/fittrackee_client/src/components/Common/Stats/CustomTooltip.jsx +++ b/fittrackee_client/src/components/Common/Stats/CustomTooltip.jsx @@ -1,6 +1,6 @@ import React from 'react' -import { formatDuration } from '../../../utils' +import { formatDuration } from '../../../utils/stats' const formatValue = (displayedData, value) => displayedData === 'duration' ? formatDuration(value, true) diff --git a/fittrackee_client/src/components/Common/Stats/StatsChart.jsx b/fittrackee_client/src/components/Common/Stats/StatsChart.jsx index 63368c5f..becb639f 100644 --- a/fittrackee_client/src/components/Common/Stats/StatsChart.jsx +++ b/fittrackee_client/src/components/Common/Stats/StatsChart.jsx @@ -3,7 +3,8 @@ import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts' -import { activityColors, formatDuration } from '../../../utils' +import { activityColors } from '../../../utils/activities' +import { formatDuration } from '../../../utils/stats' import CustomTooltip from './CustomTooltip' diff --git a/fittrackee_client/src/components/Common/Stats/index.jsx b/fittrackee_client/src/components/Common/Stats/index.jsx index 3ac8e0ad..b6b33d9e 100644 --- a/fittrackee_client/src/components/Common/Stats/index.jsx +++ b/fittrackee_client/src/components/Common/Stats/index.jsx @@ -3,7 +3,7 @@ import React from 'react' import { connect } from 'react-redux' import { getStats } from '../../../actions/stats' -import { formatStats } from '../../../utils' +import { formatStats } from '../../../utils/stats' import StatsChart from './StatsChart' diff --git a/fittrackee_client/src/components/Dashboard/Calendar.jsx b/fittrackee_client/src/components/Dashboard/Calendar.jsx index ef3b1adc..a3af8034 100644 --- a/fittrackee_client/src/components/Dashboard/Calendar.jsx +++ b/fittrackee_client/src/components/Dashboard/Calendar.jsx @@ -6,7 +6,8 @@ import { connect } from 'react-redux' import { Link } from 'react-router-dom' import { getMonthActivities } from '../../actions/activities' -import { getDateWithTZ, recordsLabels } from '../../utils' +import { getDateWithTZ } from '../../utils' +import { recordsLabels } from '../../utils/activities' const getStartAndEndMonth = date => { const monthStart = dateFns.startOfMonth(date) diff --git a/fittrackee_client/src/components/Dashboard/Records.jsx b/fittrackee_client/src/components/Dashboard/Records.jsx index d3fb9be3..58505607 100644 --- a/fittrackee_client/src/components/Dashboard/Records.jsx +++ b/fittrackee_client/src/components/Dashboard/Records.jsx @@ -1,7 +1,7 @@ import React from 'react' import { Link } from 'react-router-dom' -import { formatRecord } from '../../utils' +import { formatRecord } from '../../utils/activities' export default function RecordsCard (props) { const { records, sports, user } = props diff --git a/fittrackee_client/src/utils.js b/fittrackee_client/src/utils.js deleted file mode 100644 index b12260a5..00000000 --- a/fittrackee_client/src/utils.js +++ /dev/null @@ -1,226 +0,0 @@ -import togeojson from '@mapbox/togeojson' -import { - addDays, addMonths, addYears, format, parse, startOfMonth, startOfWeek, - startOfYear -} from 'date-fns' -import { DateTime } from 'luxon' - -export const apiUrl = `${process.env.REACT_APP_API_URL}/api/` -export const thunderforestApiKey = `${ - process.env.REACT_APP_THUNDERFOREST_API_KEY - }` -export const gpxLimit = `${process.env.REACT_APP_GPX_LIMIT_IMPORT}` -export const activityColors = [ - '#55a8a3', - '#98C3A9', - '#D0838A', - '#ECC77E', - '#926692', - '#929292', - '#428bca', -] - -export const isLoggedIn = () => !!window.localStorage.authToken - -export const generateIds = arr => { - let i = 0 - return arr.map(val => { - const obj = { id: i, value: val } - i++ - return obj - }) -} - - -export const createRequest = params => { - const headers = {} - if (!params.noAuthorization) { - headers.Authorization = `Bearer ${ - window.localStorage.getItem('authToken')}` - } - if (params.type) { - headers['Content-Type'] = params.type - } - const requestParams = { - method: params.method, - headers: headers, - } - if (params.type === 'application/json' && params.body) { - requestParams.body = JSON.stringify(params.body) - } else if (params.body) { - requestParams.body = params.body - } - const request = new Request(params.url, requestParams) - return fetch(request) - .then(response => params.method === 'DELETE' - ? response - : response.json()) - .catch(error => error) -} - - -export const getGeoJson = gpxContent => { - let jsonData - if (gpxContent) { - const gpx = new DOMParser().parseFromString(gpxContent, 'text/xml') - jsonData = togeojson.gpx(gpx) - } - return { jsonData } -} - - -export const getDateWithTZ = (date, tz) => { - if (!date) { - return '' - } - const dt = DateTime.fromISO(format(date)).setZone(tz) - return parse(dt.toFormat('yyyy-MM-dd HH:mm:ss')) -} - -export const formatActivityDate = ( - dateTime, - dateFormat = null, - timeFormat = null, -) => { - if (!dateFormat) { - dateFormat = 'DD/MM/YYYY' - } - if (!timeFormat) { - timeFormat = 'HH:mm' - } - return { - activity_date: dateTime ? format(dateTime, dateFormat) : null, - activity_time: dateTime ? format(dateTime, timeFormat) : null, - } -} - -export const recordsLabels = [ - { record_type: 'AS', label: 'Avg speed' }, - { record_type: 'FD', label: 'Farest distance' }, - { record_type: 'LD', label: 'Longest duration' }, - { record_type: 'MS', label: 'Max speed' }, -] - -export const formatRecord = (record, tz) => { - let value, recordType = null - switch (record.record_type) { - case 'AS': - case 'MS': - value = `${record.value} km/h` - break - case 'FD': - value = `${record.value} km` - break - default: // 'LD' - value = record.value // eslint-disable-line prefer-destructuring - } - [recordType] = recordsLabels.filter(r => r.record_type === record.record_type) - return { - activity_date: formatActivityDate( - getDateWithTZ(record.activity_date, tz) - ).activity_date, - activity_id: record.activity_id, - id: record.id, - record_type: recordType.label, - value: value, - } -} - -export const formatDuration = (totalSeconds, formatWithDay = false) => { - let days = '0' - if (formatWithDay) { - days = String(Math.floor(totalSeconds / 86400)) - totalSeconds %= 86400 - } - const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0') - totalSeconds %= 3600 - const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0') - const seconds = String(totalSeconds % 60).padStart(2, '0') - if (formatWithDay) { - return `${ - days === '0' ? '' : `${days}d:` - }${ - hours === '00' ? '' : `${hours}h:` - }${minutes}m:${seconds}s` - } - return `${hours === '00' ? '' : `${hours}:`}${minutes}:${seconds}` -} - -export const formatChartData = chartData => { - for (let i = 0; i < chartData.length; i++) { - chartData[i].time = new Date(chartData[i].time).getTime() - chartData[i].duration = formatDuration(chartData[i].duration) - } - return chartData -} - -const xAxisFormats = [ - { duration: 'week', dateFormat: 'YYYY-MM-DD', xAxis: 'DD/MM' }, - { duration: 'month', dateFormat: 'YYYY-MM', xAxis: 'MM/YYYY' }, - { duration: 'year', dateFormat: 'YYYY', xAxis: 'YYYY' }, -] - -const dateIncrement = (duration, day) => { - switch (duration) { - case 'week': - return addDays(day, 7) - case 'year': - return addYears(day, 1) - case 'month': - default: - return addMonths(day, 1) - } -} - -const startDate = (duration, day) => { - switch (duration) { - case 'week': - return startOfWeek(day) - case 'year': - return startOfYear(day) - case 'month': - default: - return startOfMonth(day) - } -} - -export const formatStats = ( - stats, sports, params -) => { - const nbActivitiesStats = [] - const distanceStats = [] - const durationStats = [] - - for (let day = startDate(params.duration, params.start); - day <= params.end; - day = dateIncrement(params.duration, day) - ) { - const [xAxisFormat] = xAxisFormats.filter( - x => x.duration === params.duration - ) - const date = format(day, xAxisFormat.dateFormat) - const xAxis = format(day, xAxisFormat.xAxis) - const dataNbActivities = { date: xAxis } - const dataDistance = { date: xAxis } - const dataDuration = { date: xAxis } - - if (stats[date]) { - Object.keys(stats[date]).map(sportId => { - const sportLabel = sports.filter(s => s.id === +sportId)[0].label - dataNbActivities[sportLabel] = stats[date][sportId].nb_activities - dataDistance[sportLabel] = stats[date][sportId].total_distance - dataDuration[sportLabel] = stats[date][sportId].total_duration - return null - }) - } - nbActivitiesStats.push(dataNbActivities) - distanceStats.push(dataDistance) - durationStats.push(dataDuration) - } - - return { - activities: nbActivitiesStats, - distance: distanceStats, - duration: durationStats - } -} diff --git a/fittrackee_client/src/utils/activities.js b/fittrackee_client/src/utils/activities.js new file mode 100644 index 00000000..5330184e --- /dev/null +++ b/fittrackee_client/src/utils/activities.js @@ -0,0 +1,74 @@ +import { format } from 'date-fns' +import togeojson from '@mapbox/togeojson' + +import { getDateWithTZ } from './index' + +export const activityColors = [ + '#55a8a3', + '#98C3A9', + '#D0838A', + '#ECC77E', + '#926692', + '#929292', + '#428bca', +] + +export const recordsLabels = [ + { record_type: 'AS', label: 'Avg speed' }, + { record_type: 'FD', label: 'Farest distance' }, + { record_type: 'LD', label: 'Longest duration' }, + { record_type: 'MS', label: 'Max speed' }, +] + +export const getGeoJson = gpxContent => { + let jsonData + if (gpxContent) { + const gpx = new DOMParser().parseFromString(gpxContent, 'text/xml') + jsonData = togeojson.gpx(gpx) + } + return { jsonData } +} + +export const formatActivityDate = ( + dateTime, + dateFormat = null, + timeFormat = null, +) => { + if (!dateFormat) { + dateFormat = 'DD/MM/YYYY' + } + if (!timeFormat) { + timeFormat = 'HH:mm' + } + return { + activity_date: dateTime ? format(dateTime, dateFormat) : null, + activity_time: dateTime ? format(dateTime, timeFormat) : null, + } +} + +export const formatRecord = (record, tz) => { + let value + switch (record.record_type) { + case 'AS': + case 'MS': + value = `${record.value} km/h` + break + case 'FD': + value = `${record.value} km` + break + default: // 'LD' + value = record.value // eslint-disable-line prefer-destructuring + } + const [recordType] = recordsLabels.filter( + r => r.record_type === record.record_type + ) + return { + activity_date: formatActivityDate( + getDateWithTZ(record.activity_date, tz) + ).activity_date, + activity_id: record.activity_id, + id: record.id, + record_type: recordType.label, + value: value, + } +} diff --git a/fittrackee_client/src/utils/index.js b/fittrackee_client/src/utils/index.js new file mode 100644 index 00000000..d5b51049 --- /dev/null +++ b/fittrackee_client/src/utils/index.js @@ -0,0 +1,55 @@ +import { format, parse } from 'date-fns' +import { DateTime } from 'luxon' + +export const apiUrl = `${process.env.REACT_APP_API_URL}/api/` +export const thunderforestApiKey = `${ + process.env.REACT_APP_THUNDERFOREST_API_KEY + }` +export const gpxLimit = `${process.env.REACT_APP_GPX_LIMIT_IMPORT}` + +export const isLoggedIn = () => !!window.localStorage.authToken + +export const generateIds = arr => { + let i = 0 + return arr.map(val => { + const obj = { id: i, value: val } + i++ + return obj + }) +} + + +export const createRequest = params => { + const headers = {} + if (!params.noAuthorization) { + headers.Authorization = `Bearer ${ + window.localStorage.getItem('authToken')}` + } + if (params.type) { + headers['Content-Type'] = params.type + } + const requestParams = { + method: params.method, + headers: headers, + } + if (params.type === 'application/json' && params.body) { + requestParams.body = JSON.stringify(params.body) + } else if (params.body) { + requestParams.body = params.body + } + const request = new Request(params.url, requestParams) + return fetch(request) + .then(response => params.method === 'DELETE' + ? response + : response.json()) + .catch(error => error) +} + + +export const getDateWithTZ = (date, tz) => { + if (!date) { + return '' + } + const dt = DateTime.fromISO(format(date)).setZone(tz) + return parse(dt.toFormat('yyyy-MM-dd HH:mm:ss')) +} diff --git a/fittrackee_client/src/utils/stats.js b/fittrackee_client/src/utils/stats.js new file mode 100644 index 00000000..1d2b5322 --- /dev/null +++ b/fittrackee_client/src/utils/stats.js @@ -0,0 +1,108 @@ +import { + addDays, + addMonths, + addYears, format, startOfMonth, + startOfWeek, + startOfYear +} from 'date-fns' + + +const xAxisFormats = [ + { duration: 'week', dateFormat: 'YYYY-MM-DD', xAxis: 'DD/MM' }, + { duration: 'month', dateFormat: 'YYYY-MM', xAxis: 'MM/YYYY' }, + { duration: 'year', dateFormat: 'YYYY', xAxis: 'YYYY' }, +] + + +export const formatDuration = (totalSeconds, formatWithDay = false) => { + let days = '0' + if (formatWithDay) { + days = String(Math.floor(totalSeconds / 86400)) + totalSeconds %= 86400 + } + const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0') + totalSeconds %= 3600 + const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0') + const seconds = String(totalSeconds % 60).padStart(2, '0') + if (formatWithDay) { + return `${ + days === '0' ? '' : `${days}d:` + }${ + hours === '00' ? '' : `${hours}h:` + }${minutes}m:${seconds}s` + } + return `${hours === '00' ? '' : `${hours}:`}${minutes}:${seconds}` +} + +export const formatChartData = chartData => { + for (let i = 0; i < chartData.length; i++) { + chartData[i].time = new Date(chartData[i].time).getTime() + chartData[i].duration = formatDuration(chartData[i].duration) + } + return chartData +} + +const dateIncrement = (duration, day) => { + switch (duration) { + case 'week': + return addDays(day, 7) + case 'year': + return addYears(day, 1) + case 'month': + default: + return addMonths(day, 1) + } +} + +const startDate = (duration, day) => { + switch (duration) { + case 'week': + return startOfWeek(day) + case 'year': + return startOfYear(day) + case 'month': + default: + return startOfMonth(day) + } +} + +export const formatStats = ( + stats, sports, params +) => { + const nbActivitiesStats = [] + const distanceStats = [] + const durationStats = [] + + for (let day = startDate(params.duration, params.start); + day <= params.end; + day = dateIncrement(params.duration, day) + ) { + const [xAxisFormat] = xAxisFormats.filter( + x => x.duration === params.duration + ) + const date = format(day, xAxisFormat.dateFormat) + const xAxis = format(day, xAxisFormat.xAxis) + const dataNbActivities = { date: xAxis } + const dataDistance = { date: xAxis } + const dataDuration = { date: xAxis } + + if (stats[date]) { + Object.keys(stats[date]).map(sportId => { + const sportLabel = sports.filter(s => s.id === +sportId)[0].label + dataNbActivities[sportLabel] = stats[date][sportId].nb_activities + dataDistance[sportLabel] = stats[date][sportId].total_distance + dataDuration[sportLabel] = stats[date][sportId].total_duration + return null + }) + } + nbActivitiesStats.push(dataNbActivities) + distanceStats.push(dataDistance) + durationStats.push(dataDuration) + } + + return { + activities: nbActivitiesStats, + distance: distanceStats, + duration: durationStats + } +}