Skip to content

Commit

Permalink
feat: export
Browse files Browse the repository at this point in the history
Added the ability to export a report to csv, html, or md.
  • Loading branch information
Jeff Lanzarotta committed Jan 8, 2025
1 parent 743f5ff commit f2a320c
Show file tree
Hide file tree
Showing 19 changed files with 196 additions and 48 deletions.
23 changes: 18 additions & 5 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ image:https://img.shields.io/github/go-mod/go-version/jlanzarotta/khronos[Go Ver

= Khronos
:toc: preamble
:toclevels: 6
:toclevels: 7
:icons: font
:sectnums:
:numbered:
Expand Down Expand Up @@ -561,7 +561,7 @@ $ k report --yesterday

===== --date

By specifying the option `--date`, this tells Khronos you would the report specifically for the given date. The date MUST be in the following format `YYYY-mm-dd`.
By specifying the option `--date`, this tells Khronos you would like the report specifically for the given date. The date MUST be in the following format `YYYY-mm-dd`.

[source, shell]
----
Expand All @@ -570,16 +570,29 @@ $ k report --date 2024-10-11

===== --no-rounding

By specifying the option `--no-rounding`, this tells Khronos you would
the all the duration to be their original, unrounded values. This option is good it you have durations that are
less than the value you have configured for rounding.
By specifying the option `--no-rounding`, this tells Khronos you would the all
the duration to be their original, unrounded values. This option is good it you
have durations that are less than the value you have configured for rounding.

[source, shell]
----
$ k report --from 2019-04-01 --to 2019-04-13 --no-rounding
$ k report --previous-week --no-rounding
----

==== --export type

By specifying the option '--export', this tells Khronos you would like export the report to one three types, CSV, HTML, and Mark Down. The default is CSV.

[source, shell]
----
$ k report --current-week --export --type csv
$ k report --previous-week --export --type html
$ k report --export --type md
----

These commands will create a unique report file with the extension associated with the type you specified. CSV produces a file ending in .csv, HTML produces a file ending in .html, and MD produces a file ending in .md.

=== stretch

Stretches the last entry to the current or specified date/time.
Expand Down
5 changes: 2 additions & 3 deletions cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ import (
var addCmd = &cobra.Command{
Use: "add [project+task...]",
Args: cobra.MaximumNArgs(1),
Short: "Add a completed entry",
Long: `Once you have completed a entry (project+task), use this command to add that newly
completed task to the database with an optional note.`,
Short: constants.ADD_SHORT_DESCRIPTION,
Long: constants.ADD_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runAdd(cmd, args)
},
Expand Down
5 changes: 2 additions & 3 deletions cmd/amend.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ import (
var amendCmd = &cobra.Command{
Use: "amend",
Args: cobra.MaximumNArgs(1),
Short: "Amend an entry",
Long: `Amend is a convenient way to modify an entry, default is the last
entry. It lets you modify the project, task, and/or datetime.`,
Short: constants.AMEND_SHORT_DESCRIPTION,
Long: constants.AMEND_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runAmend(cmd, args)
},
Expand Down
4 changes: 2 additions & 2 deletions cmd/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ var backendCmd = &cobra.Command{
Use: "backend",
Aliases: []string{"b", "back"},
Args: cobra.ExactArgs(0),
Short: "Open a sqlite shell to the database",
Long: "Open a sqlite shell to the database.",
Short: constants.BACKEND_SHORT_DESCRIPTION,
Long: constants.BACKEND_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runBackend(args)
},
Expand Down
5 changes: 2 additions & 3 deletions cmd/break.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ import (
// breakCmd represents the break command
var breakCmd = &cobra.Command{
Use: "break",
Short: "Add a break",
Long: `If you just spent time on break, use this command to add that time
to the database.`,
Short: constants.BREAK_SHORT_DESCRIPTION,
Long: constants.BREAK_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runBreak(cmd, args)
},
Expand Down
5 changes: 3 additions & 2 deletions cmd/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
package cmd

import (
"khronos/constants"
"log"
"os"

Expand All @@ -41,8 +42,8 @@ import (
var configureCmd = &cobra.Command{
Use: "configure",
Aliases: []string{"c", "config", "conf"},
Short: "Write out a YAML config file",
Long: "Write out a YAML config file. Print path to config file.",
Short: constants.CONFIGURE_SHORT_DESCRIPTION,
Long: constants.CONFIGURE_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runConfigure(args)
},
Expand Down
5 changes: 3 additions & 2 deletions cmd/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
package cmd

import (
"khronos/constants"
"log"
"os/exec"

Expand All @@ -41,8 +42,8 @@ import (
// editCmd represents the edit command.
var editCmd = &cobra.Command{
Use: "edit",
Short: "Open the Khronos configuration file in your default editor",
Long: "Open the Khronos configuration file in your default editor.",
Short: constants.EDIT_SHORT_DESCRIPTION,
Long: constants.EDIT_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runEdit(cmd, args)
},
Expand Down
6 changes: 2 additions & 4 deletions cmd/hello.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ var at string
// helloCmd represents the hello command
var helloCmd = &cobra.Command{
Use: "hello",
Short: "Start time tracking for the day",
Long: `In order to have khronos start tracking time is to run this
command. It informs khronos that you would like it to start tracking
your time.`,
Short: constants.HELLO_SHORT_DESCRIPTION,
Long: constants.HELLO_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runHello(cmd, args)
},
Expand Down
4 changes: 2 additions & 2 deletions cmd/nuke.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ import (

var nukeCmd = &cobra.Command{
Use: "nuke",
Short: "Nukes entries from the sqlite database",
Long: `As you continuously add completed entries, the database continues to grow unbounded. The nuke command allows you to manage the size of your database.`,
Short: constants.NUKE_SHORT_DESCRIPTION,
Long: constants.NUKE_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runNuke(cmd, args)
},
Expand Down
72 changes: 71 additions & 1 deletion cmd/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ import (
var from string
var to string
var givenDate string

var daysOfWeek = map[string]string{}
var roundToMinutes int64
var exportFilename string = constants.EMPTY
var _cmd *cobra.Command

var exportType = models.ExportTypeCSV

// reportCmd represents the report command.
var reportCmd = &cobra.Command{
Expand Down Expand Up @@ -86,6 +89,55 @@ func dateRange(date carbon.Carbon) (start carbon.Carbon, end carbon.Carbon) {
return start, end
}

func export(title string, t table.Writer) {
exporting, _ := _cmd.Flags().GetBool(constants.EXPORT)
if exporting {
typeStr, _ := _cmd.Flags().GetString(constants.EXPORT_TYPE)
if len(strings.TrimSpace(exportFilename)) == 0 {
// Create our new export file.
exportFilename = constants.APPLICATION_NAME_LOWERCASE + "_report_" + carbon.Now().ToShortDateTimeString()
if typeStr == string(models.ExportTypeCSV) {
exportFilename += ".csv"
} else if typeStr == string(models.ExportTypeHTML) {
exportFilename += ".html"
} else {
exportFilename += ".md"
}

_, err := os.OpenFile(exportFilename, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
}

// Open the file for appending.
file, err := os.OpenFile(exportFilename, os.O_APPEND|os.O_WRONLY, 0)
if err != nil {
log.Fatal(err)
}

// Remember to close the file.
defer file.Close()

file.WriteString(title + "\n")

// Render the table to the file.
if typeStr == string(models.ExportTypeCSV) {
_, err = file.WriteString(t.RenderCSV())
} else if typeStr == string(models.ExportTypeHTML) {
_, err = file.WriteString(t.RenderHTML())
} else {
_, err = file.WriteString(t.RenderMarkdown())
}

if err != nil {
log.Fatal(err)
}

file.WriteString("\n")
}
}

func init() {
reportCmd.Flags().BoolP(constants.FLAG_NO_ROUNDING, constants.EMPTY, false, "Reports all durations in their unrounded form.")
reportCmd.Flags().BoolP(constants.FLAG_CURRENT_WEEK, constants.EMPTY, false, "Report on the current week's entries.")
Expand All @@ -97,6 +149,9 @@ func init() {
reportCmd.Flags().StringVarP(&from, constants.FLAG_FROM, constants.EMPTY, constants.EMPTY, "Specify an inclusive start date to report in "+constants.DATE_FORMAT+" format.")
reportCmd.Flags().StringVarP(&to, constants.FLAG_TO, constants.EMPTY, constants.EMPTY, "Specify an inclusive end date to report in "+constants.DATE_FORMAT+" format. If this is a day of the week, then it is the next occurrence from the start date of the report, including the start date itself.")
reportCmd.MarkFlagsRequiredTogether(constants.FLAG_FROM, constants.FLAG_TO)
reportCmd.Flags().BoolP(constants.EXPORT, constants.EMPTY, false, "Export to file.")
reportCmd.Flags().Var(&exportType, constants.EXPORT_TYPE, `Type of export file. Allowed values: "csv", "html" or "md"`)
reportCmd.MarkFlagsRequiredTogether(constants.EXPORT, constants.EXPORT_TYPE)
rootCmd.AddCommand(reportCmd)

// Here you will define your flags and configuration settings.
Expand Down Expand Up @@ -213,6 +268,9 @@ func reportByDay(entries []models.Entry) {

// Render the table.
log.Println(t.Render())

// Export table if needed.
export("report by day", t)
}

func reportByEntry(entries []models.Entry) {
Expand All @@ -237,6 +295,9 @@ func reportByEntry(entries []models.Entry) {

// Render the table.
log.Println(t.Render())

// Export table if needed.
export("report by entry", t)
}

func reportByLastEntry() {
Expand Down Expand Up @@ -302,6 +363,9 @@ func reportByProject(entries []models.Entry) {

// Render the table.
log.Println(t.Render())

// Export table if needed.
export("report by project", t)
}

func reportByTask(entries []models.Entry) {
Expand Down Expand Up @@ -354,6 +418,9 @@ func reportByTask(entries []models.Entry) {

// Render the table.
log.Println(t.Render())

// Export table if needed.
export("report by task", t)
}

func reportTotalWorkAndBreakTime(entries []models.Entry) {
Expand Down Expand Up @@ -415,6 +482,9 @@ func runReport(cmd *cobra.Command, _ []string) {
var start carbon.Carbon
var end carbon.Carbon

// Save this so we can use it in other methods.
_cmd = cmd

// See if the user asked to override round. If no, use the rounding value
// from the configuration file. Otherwise, set the rounding value to 0.
noRounding, _ := cmd.Flags().GetBool(constants.FLAG_NO_ROUNDING)
Expand Down
6 changes: 2 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ var note string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "khronos",
Short: "Simple program used to track time spent on projects and tasks.",
Long: `Khronos is a simple command line tool use to track the time you spend
on a specific project and the one or more tasks associated with that project.
It was inspired by the concepts of utt (Ultimate Time Tracker) and timetrap.`,
Short: constants.ROOT_SHORT_DESCRIPTION,
Long: constants.ROOT_LONG_DESCRIPTION,
}

// Execute adds all child commands to the root command and sets flags appropriately.
Expand Down
4 changes: 2 additions & 2 deletions cmd/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ import (
// showCmd represents the show command
var showCmd = &cobra.Command{
Use: "show",
Short: "Show various information",
Long: "Show various information.",
Short: constants.SHOW_SHORT_DESCRIPTION,
Long: constants.SHOW_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runShow(cmd, args)
},
Expand Down
4 changes: 2 additions & 2 deletions cmd/stretch.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ import (
// stretchCmd represents the stretch command
var stretchCmd = &cobra.Command{
Use: "stretch last project",
Short: "Stretch the latest entry",
Long: "Stretch the latest entry to 'now' or the whatever is specified using the 'at' flag command.",
Short: constants.STRETCH_SHORT_DESCRIPTION,
Long: constants.STRETCH_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runStretch(cmd, args)
},
Expand Down
4 changes: 2 additions & 2 deletions cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ var BuildDateTime string
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Show the version information",
Long: "Show the version information.",
Short: constants.VERSION_SHORT_DESCRIPTION,
Long: constants.VERSION_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runVersion(cmd, args)
},
Expand Down
4 changes: 2 additions & 2 deletions cmd/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import (
// webCmd represents the web command.
var webCmd = &cobra.Command{
Use: "web",
Short: "Open the Khronos website in your default browser",
Long: "Open the Khronos website in your default browser.",
Short: constants.WEB_SHORT_DESCRIPTION,
Long: constants.WEB_LONG_DESCRIPTION,
Run: func(cmd *cobra.Command, args []string) {
runWeb(cmd, args)
},
Expand Down
Loading

0 comments on commit f2a320c

Please sign in to comment.