first commit

This commit is contained in:
sML 2022-12-27 10:04:51 +01:00
commit 6b00d0d113
6 changed files with 642 additions and 0 deletions

67
README.md Normal file
View File

@ -0,0 +1,67 @@
# Intro
htask (hacker task) will be a cli task manager. Now under development.
It will use SQLite3 to storage the data.
Check changelog.txt to see the current status.
# Installation
```sh
git clone https://code.lacashita.com/sml/htask
go build htask.go
./htask.go
```
# Demo
![](htask.gif)
# Usage
htask [command] [options]
## Add tasks
| Description | Command |
| ----------- | ----------- |
| Add simple task | <kbd>htask add -t "hack my school"</kbd>|
| Add complete task | <kbd>htask add -t "hack my school" -P 0 -p hacking</kbd>|
## Delete task
| Description | Command |
| ----------- | ----------- |
| Delete task 1 | <kbd>htask delete 1</kbd>|
## Start task
| Description | Command |
| ----------- | ----------- |
| Start task 1 | <kbd>htask start 1</kbd>|
## Finish task
| Description | Command |
| ----------- | ----------- |
| Finish task 1 | <kbd>htask done 1</kbd>|
## List tasks
| Description | Command |
| ----------- | ----------- |
| Show all tasks | <kbd>htask list</kbd>|
| Show tasks filtered by project | <kbd>htask list -p "hacking"</kbd>|
| Shows all tasks P0 | <kbd>htask list -P 0</kbd>|
| Shows tasks with string | <kbd>htask list string</kbd>|
## Edit tasks
| Description | Command |
| ----------- | ----------- |
| Edit task 1 | <kbd>htask edit 1</kbd>|
## View tasks
| Description | Command |
| ----------- | ----------- |
| View task 1 | <kbd>htask view 1</kbd>|

15
go.mod Normal file
View File

@ -0,0 +1,15 @@
module htask
go 1.19
require (
github.com/fatih/color v1.13.0
github.com/mattn/go-sqlite3 v1.14.15
)
require (
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/rodaine/table v1.0.1 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
)

22
go.sum Normal file
View File

@ -0,0 +1,22 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

BIN
htask Executable file

Binary file not shown.

BIN
htask.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 KiB

538
htask.go Normal file
View File

@ -0,0 +1,538 @@
package main
import (
"flag"
"fmt"
"database/sql"
"log"
"os"
"os/exec"
"io/ioutil"
"github.com/fatih/color"
_ "github.com/mattn/go-sqlite3"
"github.com/rodaine/table"
"strconv"
"time"
)
func check(e error) {
if e != nil {
panic(e)
}
}
const help = `
(·)_(o) HaCkErTaSk
Basic help
-----------
Create task: htask add -t "My new task".
List task: htask list
List task filetered: htask list -p blog
View task: htask view <id>
Edit task: htask edit <id>
Modify task: htask mod <id> -P 1
Delete task: htask delete <id>
Mark task as Started: htask start <id>
Mark task as Done: htask done <id>
Check more help in README.md file.
`
func main() {
// Get HTASK_DB env, if doesnt exists create ~/hackertask.db as database.
var filedb string
if os.Getenv("HTASK_DB") == "" {
var homepath = os.Getenv("HOME")
filedb = homepath+"/hackertask.db"
} else {
filedb = os.Getenv("HTASK_DB")
}
checkDB(filedb)
htaskmain(len(os.Args),filedb)
}
type Task struct {
id *int
name *string
priority *string
project *string
comments *string
status *string
createdate *string
startdate *string
enddate *string
duedate *string
tags *string
}
// Main function.
func htaskmain(arguments int,filedb string){
var t Task
var existid int
sqlDB, _ := sql.Open("sqlite3",filedb)
defer sqlDB.Close()
if len(os.Args) < 2 {
fmt.Println(help)
os.Exit(0)
}
switch os.Args[1] {
case "add":
addCommand := flag.NewFlagSet("add", flag.ExitOnError)
t.priority = addCommand.String("P", "5", "Priority")
t.project = addCommand.String("p", "default", "Project")
t.name = addCommand.String("t", "", "Task name")
t.status = addCommand.String("s", "Open", "Status")
t.duedate = addCommand.String("d", "NoDate", "Due Date")
addCommand.Parse(os.Args[2:])
if *t.name == "" {
fmt.Println("Parameter -t should not be empty")
os.Exit(0)
} else {
addTask(sqlDB,*t.name,*t.status,*t.priority,*t.project,*t.duedate)
}
case "list":
listCommand := flag.NewFlagSet("list", flag.ExitOnError)
t.priority = listCommand.String("P", "%", "Priority")
t.project = listCommand.String("p", "%", "Project")
t.status = listCommand.String("s", "%", "Status")
t.duedate = listCommand.String("d", "%", "Due Date")
listCommand.Parse(os.Args[2:])
listTask(sqlDB,*t.priority,*t.project,*t.status,*t.duedate)
case "mod":
if len(os.Args) > 2{
modCommand := flag.NewFlagSet("mod", flag.ExitOnError)
t.name = modCommand.String("t", "", "Task name")
t.priority = modCommand.String("P", "", "Priority")
t.project = modCommand.String("p", "", "Project")
t.duedate = modCommand.String("d", "", "Due Date")
modCommand.Parse(os.Args[3:])
theintid, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("Parameter should be the ID.")
os.Exit(0)
}
existid = checkID(sqlDB,theintid)
if existid != 0 {
modTask(sqlDB,*t.name,*t.priority,*t.project,*t.duedate,theintid)
} else {
fmt.Println("ID dont exist.")
}
} else {
fmt.Println("Usage: htask mod <id>")
}
case "view":
if len(os.Args) > 2{
// Convert param 2 into int.
theintid, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("Parameter should be the ID.")
os.Exit(0)
}
existid = checkID(sqlDB,theintid)
if existid != 0 {
viewTask(sqlDB,theintid)
} else {
fmt.Println("ID dont exist.")
}
} else {
fmt.Println("Usage: htask view <id>")
}
case "delete":
if len(os.Args) > 2{
// Convert param 2 into int.
theintid, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("Parameter should be the ID.")
os.Exit(0)
}
existid = checkID(sqlDB,theintid)
if existid != 0 {
deleteTask(sqlDB,theintid)
} else {
fmt.Println("ID dont exist.")
}
} else {
fmt.Println("Usage: htask delete <id>")
}
case "done":
if len(os.Args) > 2{
// Convert param 2 into int.
theintid, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("Parameter should be the ID.")
os.Exit(0)
}
existid = checkID(sqlDB,theintid)
existDONE := checkStatus(sqlDB,theintid)
if existid != 0 {
if existDONE != "Done" {
doneTask(sqlDB,theintid)
} else {
fmt.Println("This task was marked as Done in the past.")
}
} else {
fmt.Println("ID dont exist")
}
} else {
fmt.Println("Usage: htask done <id>")
}
case "start":
if len(os.Args) > 2{
// Convert param 2 into int.
theintid, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("Parameter should be the ID.")
os.Exit(0)
}
existid = checkID(sqlDB,theintid)
existDONE := checkStatus(sqlDB,theintid)
if existid != 0 {
if existDONE != "Working" && existDONE != "Done" {
startTask(sqlDB,theintid)
} else {
fmt.Println("This task was started/finished in the past.")
}
} else {
fmt.Println("ID dont exist")
}
} else {
fmt.Println("Usage: htask start <id>")
}
case "edit":
if len(os.Args) > 2{
// Convert param 2 into int.
theintid, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("Parameter should be the ID.")
os.Exit(0)
}
existid = checkID(sqlDB,theintid)
if existid != 0 {
editTask(sqlDB,theintid)
} else {
fmt.Println("ID dont exist.")
}
} else {
fmt.Println("Usage: htask edit <id>")
}
default:
fmt.Println(help)
os.Exit(0)
}
}
// Func to add Task.
func addTask(db *sql.DB, name string, status string, priority string, project string, duedate string) {
date := time.Now().Local().String()
insertSQL := `INSERT INTO htask(name, status, priority, project,createdate,duedate) VALUES (?, ?, ?, ?, ?, ?)`
statement, err := db.Prepare(insertSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(name, status, priority, project,date,duedate)
if err != nil {
log.Fatalln(err.Error())
}
color.Green("[+] Task added\n")
}
// Func to mod ify Task.
func modTask(db *sql.DB, name string, priority string, project string, duedate string, id int) {
// Mod name
if name != "" {
modSQL := `UPDATE htask set name = ? where id = ?`
statement, err := db.Prepare(modSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(name,id)
if err != nil {
log.Fatalln(err.Error())
}
}
// Mod priority
if priority != "" {
modSQL := `UPDATE htask set priority = ? where id = ?`
statement, err := db.Prepare(modSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(priority,id)
if err != nil {
log.Fatalln(err.Error())
}
}
// Mod project
if project != "" {
modSQL := `UPDATE htask set project = ? where id = ?`
statement, err := db.Prepare(modSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(project,id)
if err != nil {
log.Fatalln(err.Error())
}
}
// Mod duedate
if duedate != "" {
modSQL := `UPDATE htask set duedate = ? where id = ?`
statement, err := db.Prepare(modSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(duedate,id)
if err != nil {
log.Fatalln(err.Error())
}
}
//If all ok prints:
color.Green("[+] Task modified\n")
}
// Func to show all Tasks.
func listTask(db *sql.DB, priority string, project string, status string, duedate string) {
headerFmt := color.New(color.FgMagenta, color.Underline).SprintfFunc()
columnFmt := color.New(color.FgGreen).SprintfFunc()
tbl := table.New("ID", "Priority", "Project", "Task","Status","DueDate")
tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)
row, err := db.Query("SELECT * FROM htask where id like '%' and priority like ? and project like ? and status like ? and duedate like ?",priority,project,status,duedate)
if err != nil {
log.Fatal(err)
}
defer row.Close()
fmt.Printf("\n")
for row.Next() {
var id int
var name string
var priority string
var project string
var status string
var duedate string
var comments string
var createdate string
var startdate string
var enddate string
var tags string
row.Scan(&id, &name, &priority, &project, &status, &duedate, &comments, &createdate, &startdate, &enddate, &tags)
tbl.AddRow(id,priority,project,name,status,duedate)
}
tbl.Print()
fmt.Printf("\n")
}
// Func to view comments.
func viewTask(db *sql.DB, id int) {
row, err := db.Query("SELECT comments FROM htask where id = ?",id)
if err != nil {
log.Fatal(err)
}
defer row.Close()
fmt.Printf("\n")
for row.Next() {
var comments string
row.Scan(&comments)
fmt.Println(comments)
}
fmt.Printf("\n")
}
// Func to delete task.
func deleteTask(db *sql.DB, id int) {
deleteTaskSQL := `DELETE FROM htask WHERE id = ?`
statement, err := db.Prepare(deleteTaskSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(id)
if err != nil {
log.Fatalln(err.Error())
}
color.Green("[+] Task %v deleted successfully.\n",id)
}
// Func to done task.
func doneTask(db *sql.DB, id int) {
date := time.Now().Local().String()
doneTaskSQL := `UPDATE htask set status = "Done",enddate = ? WHERE id = ?`
statement, err := db.Prepare(doneTaskSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(date,id)
if err != nil {
log.Fatalln(err.Error())
}
color.Green("[+] Task %v mark as Done.\n",id)
}
// Func to start task.
func startTask(db *sql.DB, id int) {
date := time.Now().Local().String()
startTaskSQL := `UPDATE htask set status = "Working",startdate = ? WHERE id = ?`
statement, err := db.Prepare(startTaskSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(date,id)
if err != nil {
log.Fatalln(err.Error())
}
color.Green("[+] Task %v mark as Working.\n",id)
}
// Func to edit task.
func editTask(db *sql.DB, id int) {
// Obtain current comments.
var currentcomments string
row, err := db.Query("SELECT comments from htask where id = ?",id)
if err != nil {
log.Fatal(err)
}
defer row.Close()
for row.Next() {
var comments string
row.Scan( &comments)
currentcomments = comments
}
// Create temp file with current comments
var vi string
if os.Getenv("EDITOR") == "" {
vi = "nvim"
} else {
vi = os.Getenv("EDITOR")
}
tmpDir := os.TempDir()
tmpFile, tmpFileErr := ioutil.TempFile(tmpDir, "tempFilePrefix")
if tmpFileErr != nil {
fmt.Printf("Error %s while creating tempFile", tmpFileErr)
}
path, err := exec.LookPath(vi)
if err != nil {
fmt.Printf("Error %s while looking up for %s!!", path, vi)
}
d1 := []byte(currentcomments)
err = os.WriteFile(tmpFile.Name(), d1, 0644)
check(err)
cmd := exec.Command(path, tmpFile.Name())
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
fmt.Printf("Start failed: %s", err)
}
err = cmd.Wait()
dat, err := os.ReadFile(tmpFile.Name())
check(err)
color.Green("[+] Note saved successfully.\n")
currentdata := string(dat)
// Update current comments
updateCommentsSQL := `UPDATE htask set comments = ? where id = ?`
statement, err := db.Prepare(updateCommentsSQL)
if err != nil {
log.Fatalln(err.Error())
}
_, err = statement.Exec(currentdata,id)
if err != nil {
log.Fatalln(err.Error())
}
// Delete temp file
os.Remove(tmpFile.Name())
}
// Func to check if ID exists
func checkID(db *sql.DB, id int ) int{
var total int
row, err := db.Query("SELECT count(*) as total from htask where id = ?",id)
if err != nil {
log.Fatal(err)
}
defer row.Close()
for row.Next() {
var count int
row.Scan( &count)
total = count
}
return total
}
// Func to check if task was marked as Done
func checkStatus(db *sql.DB, id int ) string{
var resultado string
row, err := db.Query("SELECT status from htask where id = ?",id)
if err != nil {
log.Fatal(err)
}
defer row.Close()
for row.Next() {
var status string
row.Scan( &status)
resultado = status
}
return resultado
}
// Func to check if DB file exists, if not, it creates the file.
func checkDB(filedb string) {
_, err := os.Stat(filedb)
//If file htask.db doesnt exist.
if err != nil {
color.Green("[+]Creating Database\n")
file, err := os.Create(filedb)
if err != nil {
log.Fatal(err.Error())
}
file.Close()
sqlDB, _ := sql.Open("sqlite3",filedb)
defer sqlDB.Close()
createTable(sqlDB)
}
}
// Func to create Database file/schema.
func createTable(db *sql.DB) {
createHtaskTableSQL := `CREATE TABLE htask (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT,
"priority" TEXT,
"project" TEXT,
"status" TEXT,
"duedate" TEXT,
"comments" TEXT,
"createdate" TEXT,
"startdate" TEXT,
"enddate" TEXT,
"tags" TEXT
);`
color.Green("[+]Creating htask table...")
statement, err := db.Prepare(createHtaskTableSQL)
if err != nil {
log.Fatal(err.Error())
}
statement.Exec()
color.Green("[+]HackerTask DB created")
}