Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 86fe6f1

Browse files
author
rltran-codex
committed
completed project
1 parent a6ff8e1 commit 86fe6f1

File tree

6 files changed

+622
-3
lines changed

6 files changed

+622
-3
lines changed

README.md

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,100 @@
11
# Task Tracker
22

3+
- [Intro](#overview)
4+
- [Installation](#installation)
5+
- [Usage](#usage)
6+
- [Add](#add)
7+
- [Update](#update)
8+
- [Delete](#delete)
9+
- [List](#list)
10+
311
# Overview
412

513
Project from [roadmap.sh](https://roadmap.sh/projects/task-tracker) built using Go.
614

15+
Create a simple TODO application that allows users to manage tasks efficiently.
16+
Each task includes an ID, subject, creation date, and last updated timestamp.
17+
The application stores tasks in a .json file, ensuring persistence across sessions.
18+
719
# Installation
820

921
1. Clone this repo `git clone [email protected]:rltran-codex/mytask-cli.git`
1022
2. `cd mytask-cli`
11-
3. `go build -o task-cli`
23+
3. Build the application
24+
25+
### Windows 10/11
26+
27+
```powershell
28+
go build -o task-cli.exe
29+
```
30+
31+
### Linux
32+
33+
```bash
34+
go build -o task-cli
35+
```
1236

1337
# Usage
1438

15-
TODO
39+
On the first usage or absence of appdata folder, the application will
40+
initialize a new `tasks.json` file.
41+
Run the application from the command line.
42+
43+
```bash
44+
> task-cli
45+
46+
Usage:
47+
task-cli <command> [options]
48+
49+
Available commands:
50+
add Add a new task
51+
update Update an existing task
52+
delete Delete a task
53+
list List tasks based on criteria
54+
55+
Use 'task-cli <command> -h' for more information on a specific command.
56+
```
57+
58+
## Add
59+
60+
```bash
61+
> task-cli add -h
62+
63+
Usage of add:
64+
-subj string
65+
REQUIRED. Define the new task's subject.
66+
```
67+
68+
## Update
69+
70+
```bash
71+
> task-cli update -h
72+
73+
Usage of update:
74+
-id int
75+
REQUIRED. Update task with id. (default -1)
76+
-stat string
77+
OPTIONAL. Update task's status.
78+
-subj string
79+
OPTIONAL. Update task's subject.
80+
```
81+
82+
## Delete
83+
84+
```bash
85+
> task-cli delete -h
86+
87+
Usage of delete:
88+
-id int
89+
REQUIRED. Delete task with id. (default -1)
90+
```
91+
92+
## List
93+
94+
```bash
95+
> task-cli list -h
96+
97+
Usage of list:
98+
-stat string
99+
OPTIONAL. List all tasks with specified status.
100+
```

filehandler/jsonhandler.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package filehandler
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"log"
8+
"os"
9+
"path/filepath"
10+
11+
"github.com/rltran-codex/mytask-cli/task"
12+
)
13+
14+
var TaskFile string
15+
16+
func handleError(err error) {
17+
if err != nil {
18+
log.Fatal(err)
19+
}
20+
}
21+
22+
func init() {
23+
// check if appdata directory exists
24+
baseDir, err := os.Getwd()
25+
if err != nil {
26+
log.Fatal(err)
27+
}
28+
appDir := filepath.Join(baseDir, "appdata")
29+
TaskFile = filepath.Join(appDir, "tasks.json")
30+
31+
os.Mkdir(appDir, 0744)
32+
if _, err := os.Stat(TaskFile); os.IsNotExist(err) {
33+
f, err := os.Create(TaskFile)
34+
if err != nil {
35+
log.Fatal(err)
36+
}
37+
defer f.Close()
38+
}
39+
}
40+
41+
func ReadJson(obj *task.Tasks) {
42+
f, err := os.Open(TaskFile)
43+
handleError(err)
44+
defer f.Close()
45+
46+
// load .json into a temp struct
47+
temp := struct {
48+
TodoList map[string]*task.Task `json:"todo_list"`
49+
}{}
50+
obj.TodoList = make(map[int]*task.Task)
51+
decoder := json.NewDecoder(f)
52+
err = decoder.Decode(&temp)
53+
if err != nil {
54+
log.Println("WARNING: task.json was empty or corrupted, starting fresh")
55+
return
56+
}
57+
58+
// filter out any keys that are not int
59+
for k, v := range temp.TodoList {
60+
var key int
61+
if i, err := fmt.Sscanf(k, "%d", &key); i == 1 && err == nil {
62+
obj.TodoList[key] = v
63+
}
64+
handleError(err)
65+
}
66+
}
67+
68+
func Update(data task.Tasks) {
69+
// use a temp file to try and write
70+
tempDir, err := os.MkdirTemp("", "task-cli-")
71+
handleError(err)
72+
defer os.RemoveAll(tempDir)
73+
74+
tempFile, err := os.CreateTemp(tempDir, "task-tmp-*.json")
75+
handleError(err)
76+
defer tempFile.Close()
77+
78+
encoder := json.NewEncoder(tempFile)
79+
err = encoder.Encode(data)
80+
handleError(err)
81+
82+
out, err := os.OpenFile(TaskFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0777)
83+
handleError(err)
84+
defer out.Close()
85+
86+
// copy task-tmp-*.json to task.json
87+
tempFile.Seek(0, io.SeekStart)
88+
_, err = io.Copy(out, tempFile)
89+
handleError(err)
90+
}

main.go

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,146 @@
11
package main
22

3+
import (
4+
"flag"
5+
"fmt"
6+
"log"
7+
"os"
8+
9+
"github.com/rltran-codex/mytask-cli/filehandler"
10+
"github.com/rltran-codex/mytask-cli/task"
11+
)
12+
13+
var (
14+
addCmd *flag.FlagSet
15+
updateCmd *flag.FlagSet
16+
deleteCmd *flag.FlagSet
17+
listCmd *flag.FlagSet
18+
)
19+
20+
var Tasks *task.Tasks
21+
322
func main() {
4-
// TODO
23+
// make a flag group for add
24+
addCmd = flag.NewFlagSet("add", flag.ExitOnError)
25+
subAdd := addCmd.String("subj", "", "REQUIRED. Define the new task's subject.")
26+
27+
// make a flag group for update
28+
updateCmd = flag.NewFlagSet("update", flag.ExitOnError)
29+
uId := updateCmd.Int("id", -1, "REQUIRED. Update task with id.")
30+
subUpdate := updateCmd.String("subj", "", "OPTIONAL. Update task's subject.")
31+
statusUpdate := updateCmd.String("stat", "", "OPTIONAL. Update task's status. [todo, in-progress, done]")
32+
33+
// make a flag group for delete
34+
deleteCmd = flag.NewFlagSet("delete", flag.ExitOnError)
35+
dId := deleteCmd.Int("id", -1, "REQUIRED. Delete task with id.")
36+
37+
// make a flag group for list
38+
listCmd = flag.NewFlagSet("list", flag.ExitOnError)
39+
statusList := listCmd.String("stat", "", "OPTIONAL. List all tasks with specified status. [todo, in-progress, done]")
40+
41+
// init Tasks
42+
Tasks = &task.Tasks{}
43+
filehandler.ReadJson(Tasks)
44+
defer filehandler.Update(*Tasks)
45+
46+
if len(os.Args) <= 1 {
47+
printUsage()
48+
}
49+
switch os.Args[1] {
50+
case "add":
51+
addCmd.Parse(os.Args[2:])
52+
AddHandler(*subAdd)
53+
case "update":
54+
updateCmd.Parse(os.Args[2:])
55+
UpdateHandler(*uId, *subUpdate, *statusUpdate)
56+
case "delete":
57+
deleteCmd.Parse(os.Args[2:])
58+
DeleteHandler(*dId)
59+
case "list":
60+
listCmd.Parse(os.Args[2:])
61+
ListHandler(*statusList)
62+
default:
63+
printUsage()
64+
}
65+
}
66+
67+
func printUsage() {
68+
cmds := []struct {
69+
cmd string
70+
desc string
71+
}{
72+
{cmd: "add", desc: "Add a new task"},
73+
{cmd: "update", desc: "Update an existing task"},
74+
{cmd: "delete", desc: "Delete a task"},
75+
{cmd: "list", desc: "List tasks based on criteria"},
76+
}
77+
fmt.Println("Usage:")
78+
fmt.Println(" task-cli <command> [options]")
79+
fmt.Println("")
80+
fmt.Println("Available commands:")
81+
for _, c := range cmds {
82+
fmt.Printf("%-2s%-10s %10s\n", "", c.cmd, c.desc)
83+
}
84+
fmt.Println("")
85+
fmt.Println("Use 'task-cli <command> -h' for more information on a specific command.")
86+
os.Exit(0)
87+
}
88+
89+
func AddHandler(subject string) {
90+
if len(subject) == 0 {
91+
log.Fatalln("cannot add task with empty subject.")
92+
}
93+
newId := Tasks.AddTask(subject)
94+
log.Printf("SUCCESS - added new task (ID: %d)", newId)
95+
}
96+
97+
func UpdateHandler(uId int, subUpdate string, statusUpdate string) {
98+
var s task.Status
99+
if len(statusUpdate) > 0 {
100+
parseStatus(statusUpdate, &s)
101+
}
102+
103+
if target, err := Tasks.UpdateTaskById(uId, subUpdate, s); err == nil {
104+
log.Printf("SUCCESS - updated task (ID: %d)", uId)
105+
log.Println(target)
106+
} else {
107+
log.Fatal(err)
108+
}
109+
}
110+
111+
func DeleteHandler(dId int) {
112+
if target, err := Tasks.DeleteTaskById(dId); err == nil {
113+
log.Printf("SUCCESS - deleted task (ID: %d)", dId)
114+
log.Println(target)
115+
} else {
116+
log.Fatal(err)
117+
}
118+
}
119+
120+
func ListHandler(status string) {
121+
var list []task.Task
122+
if len(status) == 0 {
123+
list = Tasks.FetchAllTasks()
124+
} else {
125+
var s task.Status
126+
parseStatus(status, &s)
127+
list = Tasks.FetchTaskByStatus(s)
128+
}
129+
130+
for i := range list {
131+
fmt.Println(list[i])
132+
}
133+
}
134+
135+
func parseStatus(s string, st *task.Status) {
136+
switch s {
137+
case "done":
138+
*st = task.DONE
139+
case "todo":
140+
*st = task.TODO
141+
case "in-progress":
142+
*st = task.IN_PROG
143+
default:
144+
log.Fatal("invalid status given")
145+
}
5146
}

task-cli

2.99 MB
Binary file not shown.

0 commit comments

Comments
 (0)