htask/htask.go
2022-12-27 10:04:51 +01:00

539 lines
14 KiB
Go

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")
}