Skip to content

Commit

Permalink
Merge pull request #143 from KonstantinGasser/report_filter_module#140
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikbraun authored Jul 10, 2021
2 parents 65d160e + 29bcea3 commit 6853e6b
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 23 deletions.
32 changes: 22 additions & 10 deletions cli/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import (
)

type reportOptions struct {
isBillable bool
projectKey string
outputFormat string
filePath string
startTime string
endTime string
isBillable bool
isNonBillable bool
projectKey string
outputFormat string
filePath string
startTime string
endTime string
}

func generateReportCommand(t *core.Timetrace) *cobra.Command {
Expand Down Expand Up @@ -57,8 +58,16 @@ func generateReportCommand(t *core.Timetrace) *cobra.Command {
if options.projectKey != "" {
filter = append(filter, core.FilterByProject(options.projectKey))
}
// wont hurt table will just be empty but makes sense to let the user know
if options.isBillable && options.isNonBillable {
out.Err("Cannot filter for billable and none billable records")
return
}
if options.isBillable {
filter = append(filter, core.FilterBillable)
filter = append(filter, core.FilterBillable(true))
}
if options.isNonBillable {
filter = append(filter, core.FilterBillable(false))
}

report, err := t.Report(filter...)
Expand All @@ -79,13 +88,13 @@ func generateReportCommand(t *core.Timetrace) *cobra.Command {
default:
projects, total := report.Table()
out.Table(
[]string{"Project", "Date", "Start", "End", "Billable", "Total"},
[]string{"Project", "Module", "Date", "Start", "End", "Billable", "Total"},
projects,
[]string{"", "", "", "", "TOTAL", total},
[]string{"", "", "", "", "", "TOTAL", total},
out.TableWithCellMerge(0), // merge cells over "Project" (index:0) column
out.TableFooterColor(
tablewriter.Colors{}, tablewriter.Colors{},
tablewriter.Colors{}, tablewriter.Colors{},
tablewriter.Colors{}, tablewriter.Colors{}, tablewriter.Colors{},
tablewriter.Colors{tablewriter.Bold}, // text "TOTAL"
tablewriter.Colors{tablewriter.FgGreenColor}), // digit of "TOTAL"
)
Expand All @@ -96,6 +105,9 @@ func generateReportCommand(t *core.Timetrace) *cobra.Command {
report.Flags().BoolVarP(&options.isBillable, "billable", "b",
false, "filter for only billable records")

report.Flags().BoolVarP(&options.isNonBillable, "non-billable", "n",
false, "filter for only none billable records")

report.Flags().StringVarP(&options.startTime, "start", "s",
"", "filter records from a given start date <YYYY-MM-DD>")

Expand Down
55 changes: 42 additions & 13 deletions core/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package core
import (
"encoding/json"
"fmt"
"strings"
"time"
)

Expand All @@ -14,15 +15,32 @@ func FilterNoneNilEndTime(r *Record) bool {
return r.End != nil
}

// FilterBillable returns true if a records is listed as billable
func FilterBillable(r *Record) bool {
return r.IsBillable
// FilterBillable returns a record filter for the billable flag
// that filters based on the passed display value
func FilterBillable(display bool) func(*Record) bool {
return func(r *Record) bool {
return r.IsBillable == display
}
}

// FilterByProject returns true if the record matches the given project keys
// if a module is given "mod@project" filter checks if module and project match
func FilterByProject(key string) func(*Record) bool {
module := strings.Split(key, "@") // if a module is given mod@project
return func(r *Record) bool {
return r.Project.Key == key
recordModule := strings.Split(r.Project.Key, "@")
// mod@key - mod@key -> search for project with module X
if r.Project.IsModule() && len(module) > 1 {
return recordModule[0] == module[0] && recordModule[1] == module[1]
}
// key - mod@key || key - key -> search for project where key
if len(module) == 1 {
if r.Project.IsModule() {
return recordModule[1] == key
}
return r.Project.Key == key
}
return false
}
}

Expand Down Expand Up @@ -66,18 +84,24 @@ type Reporter struct {

// sortAndMerge assigns each record in the given slice to the correct project key in the
// Reporter.report map and computes each projects total time.Duration
// projects with module will be grouped by
func (r *Reporter) sortAndMerge(records []*Record) {
for _, record := range records {
cached, ok := r.report[record.Project.Key]
key := record.Project.Key
keyParts := strings.Split(key, "@")
if len(keyParts) > 1 {
key = keyParts[1]
}
cached, ok := r.report[key]
if !ok {
r.report[record.Project.Key] = []*Record{record}
r.totals[record.Project.Key] = record.End.Sub(record.Start)
r.report[key] = []*Record{record}
r.totals[key] = record.End.Sub(record.Start)
continue
}
r.report[record.Project.Key] = append(cached, record)
r.report[key] = append(cached, record)
// compute updated total
tmp := r.totals[record.Project.Key] + record.End.Sub(record.Start)
r.totals[record.Project.Key] = tmp
tmp := r.totals[key] + record.End.Sub(record.Start)
r.totals[key] = tmp
}
}

Expand All @@ -90,7 +114,12 @@ func (r Reporter) Table() ([][]string, string) {

for key, records := range r.report {
for _, record := range records {
project := key
keyParts := strings.Split(record.Project.Key, "@")
module, key := "", keyParts[0]
if len(keyParts) > 1 {
module = keyParts[0]
key = keyParts[1]
}
billable := "no"
if record.IsBillable {
billable = "yes"
Expand All @@ -99,10 +128,10 @@ func (r Reporter) Table() ([][]string, string) {
start := r.t.Formatter().TimeString(record.Start)
end := r.t.Formatter().TimeString(*record.End)

rows = append(rows, []string{project, date, start, end, billable, ""})
rows = append(rows, []string{key, module, date, start, end, billable, ""})
}
// append with last row for total of tracked time for project
rows = append(rows, []string{key, "", "", "", defaultTotalSymbol, r.t.Formatter().FormatDuration(r.totals[key])})
rows = append(rows, []string{"", "", "", "", "", defaultTotalSymbol, r.t.Formatter().FormatDuration(r.totals[key])})
totalSum += r.totals[key]
}
return rows, r.t.Formatter().FormatDuration(totalSum)
Expand Down
71 changes: 71 additions & 0 deletions core/reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,74 @@ func TestReportFilterTimeRange(t *testing.T) {
}
}
}

func TestProjectFilter(t *testing.T) {
tt := []struct {
Key string
R Record
Expected bool
}{
{
Key: "test",
R: Record{
Project: &Project{
Key: "test",
},
},
Expected: true,
},
{
Key: "mod@test",
R: Record{
Project: &Project{
Key: "mod@test",
},
},
Expected: true,
},
{
Key: "test",
R: Record{
Project: &Project{
Key: "mod@test",
},
},
Expected: true,
},
{
Key: "mod@test",
R: Record{
Project: &Project{
Key: "test",
},
},
Expected: false,
},
{
Key: "mod@test",
R: Record{
Project: &Project{
Key: "not-test",
},
},
Expected: false,
},
{
Key: "test",
R: Record{
Project: &Project{
Key: "not-test",
},
},
Expected: false,
},
}

for _, tc := range tt {
filter := FilterByProject(tc.Key)
filtered := filter(&tc.R)
if filtered != tc.Expected {
t.Fatalf("project-filter failed: want %v, have: %v for key: %s and project.key: %s", tc.Expected, filtered, tc.Key, tc.R.Project.Key)
}
}
}

0 comments on commit 6853e6b

Please sign in to comment.