Skip to content

Commit

Permalink
feature: show total durations per hour of the day in breakdown
Browse files Browse the repository at this point in the history
  • Loading branch information
f3rno64 committed Dec 17, 2023
1 parent d399534 commit b98fbf6
Show file tree
Hide file tree
Showing 20 changed files with 173 additions and 38 deletions.
77 changes: 71 additions & 6 deletions src/commands/breakdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import sAgo from 's-ago'
import weekday from 'weekday'
import _uniq from 'lodash/uniq'
import parseDate from 'time-speak'
import { eachDayOfInterval } from 'date-fns'
import { eachHourOfInterval, eachDayOfInterval } from 'date-fns'

import DB from '../db'
import log from '../log'
Expand Down Expand Up @@ -63,6 +63,7 @@ const handler = (args: BreakdownCommandArguments) => {

const resultsPerDay: Record<string, BreakdownResult> = {}
const resultsPerWeekday: Record<string, BreakdownResult> = {}
const resultsPerHour: Record<string, BreakdownResult> = {}

const filteredSheets = U.getSheetsWithEntriesSinceDate(targetSheets, since)

Expand All @@ -71,12 +72,40 @@ const handler = (args: BreakdownCommandArguments) => {

entries.forEach((entry: TimeSheetEntry): void => {
const { start, end } = entry
const days = eachDayOfInterval({
const interval = {
start,
end: end === null ? new Date() : end
})
}

const days = eachDayOfInterval(interval)
const hours = eachHourOfInterval(interval).map((date: Date): number =>
date.getHours()
)

days.forEach((date: Date): void => {
hours.forEach((hour: number): void => {
const hourStr = hour > 11 ? `${hour - 11}pm` : `${hour + 1}am`
const duration = U.getEntryDurationInHour(entry, date, hour)

if (typeof resultsPerHour[hourStr] === 'undefined') {
resultsPerHour[hourStr] = {
date,
duration,
sheets: [sheet],
entries: [entry]
}
} else {
const resultEntry = resultsPerHour[hourStr]

resultsPerHour[hourStr] = {
...resultEntry,
duration: resultEntry.duration + duration,
entries: [...resultEntry.entries, entry],
sheets: _uniq([...resultEntry.sheets, sheet])
}
}
})

const dateKey = date.toLocaleDateString()
const dateWeekday = weekday(date.getDay() + 1)
const duration = U.getEntryDurationInDay(entry, date)
Expand Down Expand Up @@ -120,10 +149,11 @@ const handler = (args: BreakdownCommandArguments) => {
})
})

const resultsPerDayItems = Object.values(resultsPerDay)
const dayResults = Object.values(resultsPerDay)
const weekdayResults = Object.keys(resultsPerWeekday)
const hourResults = Object.keys(resultsPerHour)

if (resultsPerDayItems.length === 0) {
if (dayResults.length === 0) {
throw new Error('No results found')
}

Expand All @@ -144,8 +174,10 @@ const handler = (args: BreakdownCommandArguments) => {

const resultsPerDayOutputRows: string[][] = []
const resultsPerWeekdayOutputRows: string[][] = []
const resultsPerHourOutputRows: string[][] = []

resultsPerDayItems.forEach(({ date, duration, sheets, entries }): void => {
dayResults.sort(({ date: a }, { date: b }): number => (a > b ? 1 : -1))
dayResults.forEach(({ date, duration, sheets, entries }): void => {
const weekdayUI = `(${weekday(date.getDay() + 1)})`
const dateUI = ago ? sAgo(date) : date.toLocaleDateString()
const durationUI = U.getDurationLangString(duration, humanize)
Expand All @@ -162,6 +194,7 @@ const handler = (args: BreakdownCommandArguments) => {
])
})

weekdayResults.sort((a: string, b: string) => a.localeCompare(b))
weekdayResults.forEach((weekdayStr: string): void => {
const result = resultsPerWeekday[weekdayStr]
const { duration, sheets, entries } = result
Expand All @@ -178,11 +211,43 @@ const handler = (args: BreakdownCommandArguments) => {
])
})

hourResults.sort((a: string, b: string) => {
const aHour = a.includes('am')
? +a.substring(0, a.length - 2)
: +a.substring(0, a.length - 2) + 12

const bHour = b.includes('am')
? +b.substring(0, b.length - 2)
: +b.substring(0, b.length - 2) + 12

return aHour - bHour
})

hourResults.forEach((hourStr: string): void => {
const result = resultsPerHour[hourStr]
const { duration, sheets, entries } = result
const durationUI = U.getDurationLangString(duration, humanize)
const sheetCountUI = U.getPluralizedArrayLength(sheets, 'sheet')
const entryCountUI = U.getPluralizedArrayLength(entries, 'entry')

resultsPerHourOutputRows.push([
C.clHighlightRed(' *'),
C.clHighlight(hourStr),
C.clText(entryCountUI),
C.clText(sheetCountUI),
C.clDuration(durationUI)
])
})

P.printJustifiedContent(resultsPerDayOutputRows)
log('')
log(C.clText(' = Totals per Week Day ='))
log('')
P.printJustifiedContent(resultsPerWeekdayOutputRows)
log('')
log(C.clText(' = Totals per Hour ='))
log('')
P.printJustifiedContent(resultsPerHourOutputRows)
}

export { handler }
Expand Down
2 changes: 1 addition & 1 deletion src/commands/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const handler = (args: ListCommandArgs) => {
const sinceDate = !_isEmpty(since)
? parseDate(since)
: today
? D.getStartDate()
? D.getStartOfDayDate()
: null

const filteredSheets =
Expand Down
2 changes: 1 addition & 1 deletion src/commands/sheets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const handler = async (args: SheetsCommandArgs) => {
const sinceDate = !_isEmpty(since)
? parseDate(since)
: today
? D.getStartDate()
? D.getStartOfDayDate()
: null

const filteredSheets =
Expand Down
2 changes: 1 addition & 1 deletion src/commands/today.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const handler = (args: TodayCommandArguments) => {

const sheetsWithEntriesForToday = U.getSheetsWithEntriesSinceDate(
sheets,
D.getStartDate()
D.getStartOfDayDate()
)

if (sheetsWithEntriesForToday.length === 0) {
Expand Down
6 changes: 3 additions & 3 deletions src/commands/week.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ interface WeekCommandArguments {

const DAY_MS = 24 * 60 * 60 * 1000
const LAST_WEEK_DATE = new Date(
+D.getStartDate(new Date(Date.now() - 7 * 24 * 60 * 60 * 1000))
+D.getStartOfDayDate(new Date(Date.now() - 7 * 24 * 60 * 60 * 1000))
)

const getSheetsWithEntriesInLastWeek = (sheets: TimeSheet[]) => {
const startOfOneWeekAgo = D.getStartDate(LAST_WEEK_DATE)
const endOfToday = D.getEndDate()
const startOfOneWeekAgo = D.getStartOfDayDate(LAST_WEEK_DATE)
const endOfToday = D.getEndOfDayDate()

return sheets
.filter(({ entries }) => entries.length > 0)
Expand Down
4 changes: 2 additions & 2 deletions src/commands/yesterday.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ interface YesterdayCommandArguments {
const isEntryForYesterday = (entry: TimeSheetEntry): boolean => {
const { start, end } = entry
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000)
const startOfYesterday = D.getStartDate(yesterday)
const endOfYesterday = D.getEndDate(yesterday)
const startOfYesterday = D.getStartOfDayDate(yesterday)
const endOfYesterday = D.getEndOfDayDate(yesterday)

return (
+start >= +startOfYesterday && (end === null || +end <= +endOfYesterday)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _isDate from 'lodash/isDate'

const getEndDate = (date?: Date): Date => {
const getEndOfDayDate = (date?: Date): Date => {
const d = _isDate(date) ? new Date(+date) : new Date()

d.setHours(23)
Expand All @@ -11,4 +11,4 @@ const getEndDate = (date?: Date): Date => {
return d
}

export default getEndDate
export default getEndOfDayDate
14 changes: 14 additions & 0 deletions src/dates/get_end_of_hour_date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import _isDate from 'lodash/isDate'

const getEndOfHourDate = (hour: number, date?: Date): Date => {
const d = _isDate(date) ? date : new Date()

d.setHours(hour)
d.setMinutes(59)
d.setSeconds(59)
d.setMilliseconds(999)

return d
}

export default getEndOfHourDate
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _isDate from 'lodash/isDate'

const getStartDate = (date?: Date): Date => {
const getStartOfDayDate = (date?: Date): Date => {
const d = _isDate(date) ? date : new Date()

d.setHours(0)
Expand All @@ -11,4 +11,4 @@ const getStartDate = (date?: Date): Date => {
return d
}

export default getStartDate
export default getStartOfDayDate
14 changes: 14 additions & 0 deletions src/dates/get_start_of_hour_date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import _isDate from 'lodash/isDate'

const getStartOfHourDate = (hour: number, date?: Date): Date => {
const d = _isDate(date) ? date : new Date()

d.setHours(hour)
d.setMinutes(0)
d.setSeconds(0)
d.setMilliseconds(0)

return d
}

export default getStartOfHourDate
14 changes: 9 additions & 5 deletions src/dates/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import getDaysMS from './get_days_ms'
import getHoursMS from './get_hours_ms'
import getEndDate from './get_end_date'
import getStartDate from './get_start_date'
import getPastDayDate from './get_past_day_date'
import getEndOfDayDate from './get_end_of_day_date'
import getFutureDayDate from './get_future_day_date'
import getEndOfHourDate from './get_end_of_hour_date'
import getStartOfDayDate from './get_start_of_day_date'
import getStartOfHourDate from './get_start_of_hour_date'

export {
getDaysMS,
getHoursMS,
getEndDate,
getStartDate,
getPastDayDate,
getFutureDayDate
getEndOfDayDate,
getFutureDayDate,
getEndOfHourDate,
getStartOfDayDate,
getStartOfHourDate
}
4 changes: 2 additions & 2 deletions src/entries/is_entry_today.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { type TimeSheetEntry } from '../types'

const isEntryToday = (entry: TimeSheetEntry): boolean => {
const { start, end } = entry
const startOfToday = D.getStartDate()
const endOfToday = D.getEndDate()
const startOfToday = D.getStartOfDayDate()
const endOfToday = D.getEndOfDayDate()

return +start >= +startOfToday && (end === null || +end <= +endOfToday)
}
Expand Down
12 changes: 7 additions & 5 deletions src/print/columns/get_sheet_entry_columns.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ago from 's-ago'
import colors from 'colors'
import _isEmpty from 'lodash/isEmpty'
import _compact from 'lodash/compact'

import * as C from '../../color'
import * as U from '../../utils'
Expand Down Expand Up @@ -30,22 +31,23 @@ const getSheetEntryColumns = (
end === null
? ''
: C.clDate(
printDateAgo ? ago(start) : new Date(end).toLocaleDateString()
printDateAgo ? ago(end) : new Date(end).toLocaleDateString()
)

const dateUI = end === null ? startUI : endUI
const sheetNamePrefix =
typeof sheetName === 'undefined' || _isEmpty(sheetName)
? ''
: `${C.clText('sheet')} ${C.clSheet(sheetName)}`

return [
return _compact([
' ',
sheetNamePrefix,
`(${idUI})`,
`[${durationUI}]`,
`started ${dateUI}`,
`started ${startUI}`,
end === null ? null : `ended ${endUI}`,
descriptionUI
].map((value: string): string =>
]).map((value: string): string =>
isActive ? colors.bold.underline(value) : value
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/print/justified_content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import log from '../log'

type ColumnWidths = Record<number, number>

const DEFAULT_PADDING = 0
const DEFAULT_PADDING = 1

const printJustifiedContent = (
rows: Array<string[]>,
Expand Down
4 changes: 2 additions & 2 deletions src/tests/utils/dates/get_end_date.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-env mocha */

import { expect } from 'chai'
import { getEndDate } from '../../../dates'
import { getEndOfDayDate } from '../../../dates'

describe('utils:dates:get_end_date', () => {
it('returns a date set to the end of the provided date', () => {
const date = new Date()
const result = getEndDate(date)
const result = getEndOfDayDate(date)

expect(result.getFullYear()).to.equal(date.getFullYear())
expect(result.getMonth()).to.equal(date.getMonth())
Expand Down
4 changes: 2 additions & 2 deletions src/tests/utils/dates/get_start_date.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-env mocha */

import { expect } from 'chai'
import { getStartDate } from '../../../dates'
import { getStartOfDayDate } from '../../../dates'

describe('utils:dates:get_start_date', () => {
it('returns a date set to the start of the provided date', () => {
const date = new Date()
const result = getStartDate(date)
const result = getStartOfDayDate(date)

expect(result.getFullYear()).to.equal(date.getFullYear())
expect(result.getMonth()).to.equal(date.getMonth())
Expand Down
2 changes: 1 addition & 1 deletion src/tests/utils/get_entry_duration_in_day.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as D from '../../dates'
import { type TimeSheetEntry } from '../../types'
import { getEntryDurationInDay } from '../../utils'

const YESTERDAY = D.getStartDate(D.getPastDayDate(1))
const YESTERDAY = D.getStartOfDayDate(D.getPastDayDate(1))
const YESTERDAY_MS = +YESTERDAY

describe('utils:get_entry_duration_in_day', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/get_entry_duration_in_day.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const getEntryDurationInDay = (
const { start, end } = entry

const dayDate = _isFinite(day) ? new Date(day) : (day as Date)
const dayDateStart = D.getStartDate(dayDate)
const dayDateEnd = D.getEndDate(dayDate)
const dayDateStart = D.getStartOfDayDate(dayDate)
const dayDateEnd = D.getEndOfDayDate(dayDate)

if (+start < +dayDateStart && end !== null && +end < +dayDateStart) {
return 0
Expand Down
Loading

0 comments on commit b98fbf6

Please sign in to comment.