1package cli 2 3import ( 4 "strconv" 5 "time" 6 7 "github.com/dominikbraun/timetrace/core" 8 "github.com/dominikbraun/timetrace/out" 9 10 "github.com/spf13/cobra" 11) 12 13func listCommand(t *core.Timetrace) *cobra.Command { 14 list := &cobra.Command{ 15 Use: "list", 16 Short: "List all resources", 17 Run: func(cmd *cobra.Command, args []string) { 18 _ = cmd.Help() 19 }, 20 } 21 22 list.AddCommand(listProjectsCommand(t)) 23 list.AddCommand(listRecordsCommand(t)) 24 25 return list 26} 27 28func listProjectsCommand(t *core.Timetrace) *cobra.Command { 29 listProjects := &cobra.Command{ 30 Use: "projects", 31 Short: "List all projects", 32 Run: func(cmd *cobra.Command, args []string) { 33 allProjects, err := t.ListProjects() 34 if err != nil { 35 out.Err("Failed to list projects: %s", err.Error()) 36 return 37 } 38 39 // remove all modules from the project list 40 parentProjects := removeModules(allProjects) 41 42 rows := make([][]string, len(parentProjects)) 43 44 for i, project := range parentProjects { 45 allModules, err := t.ListProjectModules(project) 46 if err != nil { 47 out.Err("Failed to load project modules: %s", err.Error()) 48 return 49 } 50 rows[i] = make([]string, 3) 51 rows[i][0] = strconv.Itoa(i + 1) 52 rows[i][1] = project.Key 53 rows[i][2] = allModules 54 } 55 56 out.Table([]string{"#", "Key", "Modules"}, rows, nil) 57 }, 58 } 59 60 return listProjects 61} 62 63type listRecordsOptions struct { 64 isOnlyDisplayingBillable bool 65 projectKeyFilter string 66} 67 68func listRecordsCommand(t *core.Timetrace) *cobra.Command { 69 var options listRecordsOptions 70 71 listRecords := &cobra.Command{ 72 Use: "records {<YYYY-MM-DD>|today|yesterday}", 73 Short: "List all records from a date", 74 Args: cobra.ExactArgs(1), 75 Run: func(cmd *cobra.Command, args []string) { 76 date, err := t.Formatter().ParseDate(args[0]) 77 if err != nil { 78 out.Err("failed to parse date: %s", err.Error()) 79 return 80 } 81 82 records, err := t.ListRecords(date) 83 if err != nil { 84 out.Err("failed to list records: %s", err.Error()) 85 return 86 } 87 88 if len(options.projectKeyFilter) > 0 { 89 records = filterProjectRecords(records, options.projectKeyFilter) 90 } 91 92 if options.isOnlyDisplayingBillable { 93 records = filterBillableRecords(records) 94 } 95 96 rows := make([][]string, len(records)) 97 98 for i, record := range records { 99 end := defaultString 100 if record.End != nil { 101 end = t.Formatter().TimeString(*record.End) 102 } 103 104 billable := defaultBool 105 106 if record.IsBillable { 107 billable = "yes" 108 } 109 110 rows[i] = make([]string, 7) 111 rows[i][0] = strconv.Itoa(len(records) - i) 112 rows[i][1] = t.Formatter().RecordKey(record) 113 rows[i][2] = record.Project.Key 114 rows[i][3] = t.Formatter().TimeString(record.Start) 115 rows[i][4] = end 116 rows[i][5] = billable 117 rows[i][6] = t.Formatter().FormatTags(record.Tags) 118 } 119 120 footer := make([]string, 7) 121 footer[len(footer)-2] = "Total: " 122 footer[len(footer)-1] = t.Formatter().FormatDuration(getTotalTrackedTime(records)) 123 124 out.Table([]string{"#", "Key", "Project", "Start", "End", "Billable", "Tags"}, rows, footer) 125 }, 126 } 127 128 listRecords.Flags().BoolVarP(&options.isOnlyDisplayingBillable, "billable", "b", 129 false, `only display billable records`) 130 131 listRecords.Flags().StringVarP(&options.projectKeyFilter, "project", "p", 132 "", "filter by project key") 133 134 return listRecords 135} 136 137func filterBillableRecords(records []*core.Record) []*core.Record { 138 billableRecords := []*core.Record{} 139 for _, record := range records { 140 if record.IsBillable == true { 141 billableRecords = append(billableRecords, record) 142 } 143 } 144 return billableRecords 145} 146 147func filterProjectRecords(records []*core.Record, key string) []*core.Record { 148 projectRecords := []*core.Record{} 149 for _, record := range records { 150 if record.Project.Key == key || record.Project.Parent() == key { 151 projectRecords = append(projectRecords, record) 152 } 153 } 154 return projectRecords 155} 156 157func removeModules(allProjects []*core.Project) []*core.Project { 158 var parentProjects []*core.Project 159 for _, p := range allProjects { 160 if !p.IsModule() { 161 parentProjects = append(parentProjects, p) 162 } 163 } 164 165 return parentProjects 166} 167 168func getTotalTrackedTime(records []*core.Record) time.Duration { 169 var totalTime time.Duration 170 for _, record := range records { 171 totalTime += record.Duration() 172 } 173 return totalTime 174} 175