Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#38 revert flag #93

Merged
merged 18 commits into from
Jun 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 43 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ timetrace edit project <KEY>
|-|-|
|`KEY`|The project key.|

**Flags:**
|Flag|Short|Description|
|-|-|-|
|`--revert`|`-r`|Revert the project to it's state prior to the last edit.|

**Example:**

Edit a project called `make-coffee`:
Expand All @@ -260,6 +265,12 @@ Edit a project called `make-coffee`:
timetrace edit project make-coffee
```

Restore the project to it's state prior to the last edit:

```
timetrace edit project make-coffee --revert
```

### Edit a record

**Syntax:**
Expand All @@ -276,10 +287,11 @@ timetrace edit record {<KEY>|latest}

**Flags:**

|Flag|Description|
|-|-|
|`--plus`|Add the given duration to the record's end time, e.g. `--plus 1h 10m`|
|`--minus`|Subtract the given duration from the record's end time, e.g. `--minus 1h 10m`|
|Flag|Short|Description|
|-|-|-|
|`--plus`|`-p`|Add the given duration to the record's end time, e.g. `--plus 1h 10m`|
|`--minus`|`-m`|Subtract the given duration from the record's end time, e.g. `--minus 1h 10m`|
|`--revert`|`-r`|Revert the record to it's state prior to the last edit.|

**Example:**

Expand All @@ -295,6 +307,12 @@ Add 15 minutes to the end of the record created on May 1st, 3PM:
timetrace edit record 2021-05-01-15-00 --plus 15m
```

Restore the record to it's state prior to the last edit:

```
timetrace edit record 2021-05-01-15-00 --revert
```

Tip: You can get the record key `2021-05-01-15-00` using [`timetrace list records`](#list-all-records-from-a-date).

### Delete a project
Expand All @@ -311,6 +329,11 @@ timetrace delete project <KEY>
|-|-|
|`KEY`|The project key.|

**Flags:**
|Flag|Short|Description|
|-|-|-|
|`--revert`|`-r`|Restore a deleted project.|

**Example:**

Delete a project called `make-coffee`:
Expand All @@ -319,6 +342,12 @@ Delete a project called `make-coffee`:
timetrace delete project make-coffee
```

Restore the project to it's pre-deletion state:

```
timetrace delete project make-coffee --revert
```

### Delete a record

**Syntax:**
Expand All @@ -333,9 +362,10 @@ timetrace delete record <YYYY-MM-DD-HH-MM>
|-|-|
|`YYYY-MM-DD-HH-MM`|The start time of the desired record.|

|Flat|Description|
|-|-|
|--yes|Do not ask for confirmation|
|Flag|Short|Description|
|-|-|-|
|`--yes`| |Do not ask for confirmation|
|`--revert`|`-r`|Restore a deleted record.|

**Example:**

Expand All @@ -345,6 +375,12 @@ Delete a record created on May 1st 2021, 3:00 PM:
timetrace delete record 2021-05-01-15-00
```

Restore the record to it's pre-deletion state:

```
timetrace delete record 2021-05-01-15-00 --revert
```

### Start tracking

**Syntax:**
Expand Down
38 changes: 37 additions & 1 deletion cli/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import (

var confirmed bool

type deleteOptions struct {
Revert bool
}

func deleteCommand(t *core.Timetrace) *cobra.Command {
delete := &cobra.Command{
Use: "delete",
Expand All @@ -31,17 +35,31 @@ func deleteCommand(t *core.Timetrace) *cobra.Command {
}

func deleteProjectCommand(t *core.Timetrace) *cobra.Command {
var options deleteOptions
deleteProject := &cobra.Command{
Use: "project <KEY>",
Short: "Delete a project",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
key := args[0]
if options.Revert {
if err := t.RevertProject(key); err != nil {
out.Err("Failed to revert project: %s", err.Error())
} else {
out.Info("Project backup restored successfully")
}
return
}

project := core.Project{
Key: key,
}

if err := t.BackupProject(key); err != nil {
out.Err("Failed to backup project before deletion: %s", err.Error())
return
}

if err := t.DeleteProject(project); err != nil {
out.Err("Failed to delete %s", err.Error())
return
Expand All @@ -51,11 +69,13 @@ func deleteProjectCommand(t *core.Timetrace) *cobra.Command {
},
}

deleteProject.PersistentFlags().BoolVarP(&options.Revert, "revert", "r", false, "Restores the record to it's state prior to the last 'delete' command.")

return deleteProject
}

func deleteRecordCommand(t *core.Timetrace) *cobra.Command {

var options deleteOptions
// Depending on the use12hours setting, the command syntax either is
// `record YYYY-MM-DD-HH-MM` or `record YYYY-MM-DD-HH-MMPM`.
use := fmt.Sprintf("record %s", t.Formatter().RecordKeyLayout())
Expand All @@ -71,6 +91,15 @@ func deleteRecordCommand(t *core.Timetrace) *cobra.Command {
return
}

if options.Revert {
if err := t.RevertRecord(start); err != nil {
out.Err("Failed to revert record: %s", err.Error())
} else {
out.Info("Record backup restored successfully")
}
return
}

record, err := t.LoadRecord(start)
if err != nil {
out.Err("Failed to read record: %s", err.Error())
Expand All @@ -85,6 +114,11 @@ func deleteRecordCommand(t *core.Timetrace) *cobra.Command {
}
}

if err := t.BackupRecord(start); err != nil {
out.Err("Failed to backup record before deletion: %s", err.Error())
return
}

if err := t.DeleteRecord(*record); err != nil {
out.Err("Failed to delete %s", err.Error())
return
Expand All @@ -94,6 +128,8 @@ func deleteRecordCommand(t *core.Timetrace) *cobra.Command {
},
}

deleteRecord.PersistentFlags().BoolVarP(&options.Revert, "revert", "r", false, "Restores the record to it's state prior to the last 'delete' command.")

return deleteRecord
}

Expand Down
40 changes: 36 additions & 4 deletions cli/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,26 @@ func editCommand(t *core.Timetrace) *cobra.Command {
}

func editProjectCommand(t *core.Timetrace) *cobra.Command {
var options editOptions
editProject := &cobra.Command{
Use: "project <KEY>",
Short: "Edit a project",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
key := args[0]
if options.Revert {
if err := t.RevertProject(key); err != nil {
out.Err("Failed to revert project: %s", err.Error())
} else {
out.Info("Project backup restored successfuly")
}
return
}

if err := t.BackupProject(key); err != nil {
out.Err("Failed to backup project before edit: %s", err.Error())
return
}
out.Info("Opening %s in default editor", key)

if err := t.EditProject(key); err != nil {
Expand All @@ -44,12 +58,15 @@ func editProjectCommand(t *core.Timetrace) *cobra.Command {
},
}

editProject.PersistentFlags().BoolVarP(&options.Revert, "revert", "r", false, "Restores the project to it's state prior to the last 'edit' command.")

return editProject
}

type editOptions struct {
Plus string
Minus string
Plus string
Minus string
Revert bool
}

func editRecordCommand(t *core.Timetrace) *cobra.Command {
Expand Down Expand Up @@ -83,15 +100,29 @@ func editRecordCommand(t *core.Timetrace) *cobra.Command {
}
}

if options.Revert {
if err := t.RevertRecord(recordTime); err != nil {
out.Err("Failed to revert record: %s", err.Error())
} else {
out.Info("Record backup restored successfully")
}
return
}

if err := t.BackupRecord(recordTime); err != nil {
out.Err("Failed to backup record before edit: %s", err.Error())
return
}

if options.Minus == "" && options.Plus == "" {
out.Info("Opening %s in default editor", recordTime)
if err := t.EditRecordManual(recordTime); err != nil {
out.Err("Failed to edit project: %s", err.Error())
out.Err("Failed to edit record: %s", err.Error())
return
}
} else {
if err := t.EditRecord(recordTime, options.Plus, options.Minus); err != nil {
out.Err("Failed to edit project: %s", err.Error())
out.Err("Failed to edit record: %s", err.Error())
return
}
}
Expand All @@ -102,6 +133,7 @@ func editRecordCommand(t *core.Timetrace) *cobra.Command {

editRecord.PersistentFlags().StringVarP(&options.Plus, "plus", "p", "", "Adds the given duration to the end time of the record")
editRecord.PersistentFlags().StringVarP(&options.Minus, "minus", "m", "", "Substracts the given duration to the end time of the record")
editRecord.PersistentFlags().BoolVarP(&options.Revert, "revert", "r", false, "Restores the record to it's state prior to the last 'edit' command.")

return editRecord
}
64 changes: 60 additions & 4 deletions core/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ const (
)

var (
ErrProjectNotFound = errors.New("project not found")
ErrProjectAlreadyExists = errors.New("project already exists")
ErrParentlessModule = errors.New("no parent project for module exists, please create parent first")
ErrProjectNotFound = errors.New("project not found")
ErrBackupProjectNotFound = errors.New("backup project not found")
ErrProjectAlreadyExists = errors.New("project already exists")
ErrParentlessModule = errors.New("no parent project for module exists, please create parent first")
)

type Project struct {
Expand Down Expand Up @@ -46,6 +47,11 @@ func (t *Timetrace) LoadProject(key string) (*Project, error) {
return t.loadProject(path)
}

func (t *Timetrace) LoadBackupProject(key string) (*Project, error) {
path := t.fs.ProjectBackupFilepath(key)
return t.loadProject(path)
}

// ListProjectModules loads all modules for a project and returns their keys as a concatenated string
func (t *Timetrace) ListProjectModules(project *Project) (string, error) {
allModules, err := t.loadProjectModules(project)
Expand Down Expand Up @@ -121,7 +127,54 @@ func (t *Timetrace) SaveProject(project Project, force bool) error {
return err
}

// EditProject opens the project file in the preferred or default editor.
// BackupProject creates a backup of the given project file.
func (t *Timetrace) BackupProject(projectKey string) error {
project, err := t.LoadProject(projectKey)
if err != nil {
return err
}

path := t.fs.ProjectBackupFilepath(projectKey)

file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}

bytes, err := json.MarshalIndent(&project, "", "\t")
if err != nil {
return err
}

_, err = file.Write(bytes)

return err
}

func (t *Timetrace) RevertProject(projectKey string) error {
project, err := t.LoadBackupProject(projectKey)
if err != nil {
return err
}
// get filepath of reverted file
path := t.fs.ProjectFilepath(projectKey)

file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}

bytes, err := json.MarshalIndent(&project, "", "\t")
if err != nil {
return err
}

_, err = file.Write(bytes)

return err
}

// EditProject opens the project file in the preferred or default editor .
func (t *Timetrace) EditProject(projectKey string) error {
if _, err := t.LoadProject(projectKey); err != nil {
return err
Expand Down Expand Up @@ -154,6 +207,9 @@ func (t *Timetrace) loadProject(path string) (*Project, error) {
file, err := ioutil.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
if strings.HasSuffix(path, ".bak") {
return nil, ErrBackupProjectNotFound
}
return nil, ErrProjectNotFound
}
return nil, err
Expand Down
Loading