Skip to content

Commit

Permalink
REPORTTING: ability to add days off, and get total days of work in mo…
Browse files Browse the repository at this point in the history
…nth (#8)
  • Loading branch information
jeff-knurek authored Oct 26, 2020
1 parent 1a2fb14 commit aeec1e2
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 7 deletions.
61 changes: 61 additions & 0 deletions cmd/dayOff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package cmd

import (
"errors"
"fmt"
"time"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

const dFormat = "2006-01-02"

// dayOffCmd represents the dayOff command
var dayOffCmd = &cobra.Command{
Use: "dayOff [day]",
Short: "Add a day that isn't calcualted for total. (format: 2016-04-28)",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("requires a day (format: 2016-04-28)")
}
if validDay(args[0]) {
return nil
}
return fmt.Errorf("invalid day format (yyyy-mm-dd): %s", args[0])
},
Run: func(cmd *cobra.Command, args []string) {
addDayOff(args[0])
},
}

func init() {
rootCmd.AddCommand(dayOffCmd)
}

func validDay(day string) bool {
_, err := time.Parse(dFormat, day)
if err != nil {
return false
}
return true
}

func addDayOff(day string) {
t, _ := time.Parse(dFormat, day)
newDay := t.Format(dFormat)
days := viper.GetStringSlice("days_off")
for _, d := range days {
if d == newDay {
fmt.Println(newDay, "already exists")
return
}
}
days = append(days, newDay)

viper.Set("days_off", days)
if err := viper.WriteConfig(); err != nil {
fmt.Println("problem with updating config file", err)
}
return
}
4 changes: 3 additions & 1 deletion cmd/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ func runReport(format string) {
panic(err)
}
filename := viper.GetString("tracking_file")
daysOff := viper.GetStringSlice("days_off")

fmt.Printf("hours worked this week: %.1f \n", reporting.HoursWorkedThisWeek(filename, user.Username))
fmt.Printf("hours worked this month: %.1f \n", reporting.HoursWorkedThisMonth(filename, user.Username))
fmt.Printf("hours worked this month: %.1f \n", reporting.HoursWorkedThisMonth(filename, user.Username, daysOff))
fmt.Printf(" out of possible days: %d \n", reporting.AvailableDaysThisMonth(time.Now(), daysOff))
fmt.Println("-------------")

fmt.Println(reporting.TextCalendar(time.Now(), filename, user.Username))
Expand Down
45 changes: 40 additions & 5 deletions pkg/reporting/simple.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package reporting

import (
"fmt"
"strconv"
"time"
)
Expand Down Expand Up @@ -32,22 +33,56 @@ func sumThisWeek(data Years, t time.Time) float64 {
}

// HoursWorkedThisMonth returns the total hours documented per day for the current month
func HoursWorkedThisMonth(filename, user string) float64 {
func HoursWorkedThisMonth(filename, user string, daysOff []string) float64 {
data := getTrackedData(filename)[user]
now := time.Now()

return sumThisMonth(data, now)
return sumThisMonth(data, now, daysOff)
}

func sumThisMonth(data Years, t time.Time) float64 {
func sumThisMonth(data Years, t time.Time, daysOff []string) float64 {
thisDay := t
y := strconv.Itoa(thisDay.Year())
m := thisDay.Month().String() // uses month name - might "break" if user switches locales
days := data[y][m]

sum := 0
for _, d := range days {
sum += d
for key, d := range days {
day, _ := strconv.Atoi(key)
if !isDayOff(daysOff, fmt.Sprintf("%s-%02d-%02d", y, int(thisDay.Month()), day)) {
sum += d
}
}
return float64(sum) / 60
}

// AvailableDaysThisMonth returns the total days that could be working days
func AvailableDaysThisMonth(t time.Time, daysOff []string) int {
totalDays := 0
thisDay := t
lastDay := thisDay.Day()
// lastDayMonth := time.Date(thisDay.Year(), time.Month(int(thisDay.Month())+1), 0, 0, 0, 0, 0, time.UTC)
// lastDay := lastDayMonth.Day()

for i := 1; i <= lastDay; i++ {
currDay := time.Date(thisDay.Year(), time.Month(int(thisDay.Month())), i, 0, 0, 0, 0, time.UTC)
if isDayOff(daysOff, currDay.Format("2006-01-02")) {
continue
}
if int(currDay.Weekday()) == 0 || int(currDay.Weekday()) == 6 {
continue
}
totalDays++
}

return totalDays
}

func isDayOff(daysOff []string, day string) bool {
for _, d := range daysOff {
if d == day {
return true
}
}
return false
}
45 changes: 44 additions & 1 deletion pkg/reporting/simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func Test_sumThisMonth(t *testing.T) {
name string
data Years
t time.Time
off []string
want float64
}{
{
Expand Down Expand Up @@ -87,12 +88,54 @@ func Test_sumThisMonth(t *testing.T) {
t: dec,
want: 660 / 60,
},
{
name: "two days, one off",
data: Years{"2020": {"December": {"30": 1440, "31": 1443}}},
t: dec,
off: []string{"2020-12-31"},
want: 1440.0 / 60,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := sumThisMonth(tt.data, tt.t); got != tt.want {
if got := sumThisMonth(tt.data, tt.t, tt.off); got != tt.want {
t.Errorf("sumThisMonth() = %v, want %v", got, tt.want)
}
})
}
}

func Test_workingDays(t *testing.T) {
dec, _ := time.Parse("2006-01-02 15:04", "2020-12-31 9:59")
jan, _ := time.Parse("2006-01-02 15:04", "2021-01-02 9:59")
tests := []struct {
name string
t time.Time
off []string
want int
}{
{
name: "last day of month",
t: dec,
want: 23,
},
{
name: "2nd day of month is Saturday",
t: jan,
want: 1,
},
{
name: "last day of month, two days off",
t: dec,
off: []string{"2020-12-25", "2020-12-31"},
want: 21,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := AvailableDaysThisMonth(tt.t, tt.off); got != tt.want {
t.Errorf("workingDays() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit aeec1e2

Please sign in to comment.