TaskFlow/db/tasks.go

397 lines
13 KiB
Go
Raw Normal View History

2015-11-13 17:04:42 +08:00
package db
2016-02-01 22:34:19 +08:00
/*
Stores the database functions related to tasks like
GetTaskByID(id int)
GetTasks(status string)
DeleteAll()
*/
2015-11-13 17:04:42 +08:00
import (
"database/sql"
"html/template"
2016-01-31 22:22:00 +08:00
"log"
"strconv"
2016-01-31 22:22:00 +08:00
"strings"
"time"
2016-02-01 21:45:04 +08:00
_ "github.com/mattn/go-sqlite3" //we want to use sqlite natively
md "github.com/shurcooL/github_flavored_markdown"
"github.com/thewhitetulip/Tasks/types"
2015-11-13 17:04:42 +08:00
)
var database Database
2016-05-14 15:26:24 +08:00
var taskStatus map[string]int
2015-11-13 17:04:42 +08:00
var err error
//Database encapsulates database
type Database struct {
db *sql.DB
}
2016-05-14 15:26:24 +08:00
//Begins a transaction
func (db Database) begin() (tx *sql.Tx) {
tx, err := db.db.Begin()
if err != nil {
log.Println(err)
2016-02-01 21:45:04 +08:00
return nil
}
return tx
}
func (db Database) prepare(q string) (stmt *sql.Stmt) {
stmt, err := db.db.Prepare(q)
if err != nil {
log.Println(err)
2016-02-01 21:45:04 +08:00
return nil
}
return stmt
}
func (db Database) query(q string, args ...interface{}) (rows *sql.Rows) {
2016-01-31 22:00:31 +08:00
rows, err := db.db.Query(q, args...)
if err != nil {
log.Println(err)
2016-02-01 21:45:04 +08:00
return nil
}
return rows
}
2015-11-13 17:04:42 +08:00
func init() {
database.db, err = sql.Open("sqlite3", "./tasks.db")
taskStatus = map[string]int{"COMPLETE": 1, "PENDING": 2, "DELETED": 3}
2015-11-13 17:04:42 +08:00
if err != nil {
2016-02-01 21:45:04 +08:00
log.Fatal(err)
2015-11-13 17:04:42 +08:00
}
}
2015-11-21 14:39:15 +08:00
//Close function closes this database connection
2015-11-13 17:04:42 +08:00
func Close() {
database.db.Close()
2015-11-13 17:04:42 +08:00
}
2015-11-21 14:39:15 +08:00
//GetTasks retrieves all the tasks depending on the
//status pending or trashed or completed
2016-05-14 15:26:24 +08:00
func GetTasks(username, status, category string) (types.Context, error) {
log.Println("getting tasks for ", status)
var tasks []types.Task
var task types.Task
var TaskCreated time.Time
var context types.Context
2016-05-14 15:26:24 +08:00
var getTaskSQL string
2016-02-03 01:40:44 +08:00
var rows *sql.Rows
2015-11-21 21:20:51 +08:00
2016-05-14 15:26:24 +08:00
comments, err := GetComments(username)
2016-02-17 00:53:03 +08:00
if err != nil {
return context, err
}
basicSQL := "select t.id, title, content, created_date, priority, case when c.name is null then 'NA' else c.name end from task t, status s, user u left outer join category c on c.id=t.cat_id where u.username=? and s.id=t.task_status_id and u.id=t.user_id "
2016-05-14 15:26:24 +08:00
if category == "" {
switch status {
case "pending":
getTaskSQL = basicSQL + " and s.status='PENDING' and t.hide!=1"
2016-05-14 15:26:24 +08:00
case "deleted":
getTaskSQL = basicSQL + " and s.status='DELETED' and t.hide!=1"
2016-05-14 15:26:24 +08:00
case "completed":
getTaskSQL = basicSQL + " and s.status='COMPLETE' and t.hide!=1"
2016-05-14 15:26:24 +08:00
}
2015-11-13 17:04:42 +08:00
getTaskSQL += " order by t.created_date asc"
rows = database.query(getTaskSQL, username, username)
2016-05-14 15:26:24 +08:00
} else {
2016-02-03 01:40:44 +08:00
status = category
//This is a special case for showing tasks with null categories, we do a union query
if category == "UNCATEGORIZED" {
getTaskSQL = "select t.id, title, content, created_date, priority, 'UNCATEGORIZED' from task t, status s, user u where u.username=? and s.id=t.task_status_id and u.id=t.user_id and t.cat_id=0 and s.status='PENDING' order by priority desc, created_date asc, finish_date asc"
rows, err = database.db.Query(getTaskSQL, username)
} else {
getTaskSQL = basicSQL + " and name = ? and s.status='PENDING' order by priority desc, created_date asc, finish_date asc"
rows, err = database.db.Query(getTaskSQL, username, category)
}
2016-02-29 10:05:38 +08:00
2016-02-03 01:40:44 +08:00
if err != nil {
2016-05-14 15:26:24 +08:00
log.Println("tasks.go: something went wrong while getting query fetch tasks by category")
2016-02-03 01:40:44 +08:00
}
}
2016-05-14 15:26:24 +08:00
2015-11-13 17:04:42 +08:00
defer rows.Close()
2015-11-13 21:11:30 +08:00
for rows.Next() {
task = types.Task{}
2016-02-29 10:05:38 +08:00
err = rows.Scan(&task.Id, &task.Title, &task.Content, &TaskCreated, &task.Priority, &task.Category)
taskCompleted := 0
totalTasks := 0
if strings.HasPrefix(task.Content, "- [") {
for _, value := range strings.Split(task.Content, "\n") {
if strings.HasPrefix(value, "- [x]") {
taskCompleted += 1
}
totalTasks += 1
}
task.CompletedMsg = strconv.Itoa(taskCompleted) + " complete out of " + strconv.Itoa(totalTasks)
}
task.ContentHTML = template.HTML(md.Markdown([]byte(task.Content)))
// TaskContent = strings.Replace(TaskContent, "\n", "<br>", -1)
2015-11-13 21:11:30 +08:00
if err != nil {
log.Println(err)
2015-11-13 17:04:42 +08:00
}
if comments[task.Id] != nil {
task.Comments = comments[task.Id]
}
TaskCreated = TaskCreated.Local()
2016-08-07 12:32:47 +08:00
// if task.Priority != "1" { // if priority is not 1 then calculate, else why bother?
// CurrentTime := time.Now().Local()
// diff := CurrentTime.Sub(TaskCreated).Hours()
// if diff > 168 {
// task.IsOverdue = true // If one week then overdue by default
// }
// }
2016-03-01 02:47:14 +08:00
task.Created = TaskCreated.Format("Jan 2 2006")
tasks = append(tasks, task)
2015-11-13 17:04:42 +08:00
}
context = types.Context{Tasks: tasks, Navigation: status}
2016-02-01 21:45:04 +08:00
return context, nil
2015-11-13 17:04:42 +08:00
}
2015-11-22 11:51:29 +08:00
//GetTaskByID function gets the tasks from the ID passed to the function, used to populate EditTask
2016-05-14 15:26:24 +08:00
func GetTaskByID(username string, id int) (types.Context, error) {
2015-11-22 10:59:39 +08:00
var tasks []types.Task
2015-11-13 17:04:42 +08:00
var task types.Task
2016-01-23 21:47:34 +08:00
getTaskSQL := "select t.id, t.title, t.content, t.priority, t.hide, c.name from task t join user u left outer join category c where c.id = t.cat_id and t.user_id=u.id and t.id=? and u.username=? union select t.id, t.title, t.content, t.priority, t.hide, 'UNCATEGORIZED' from task t join user u where t.user_id=u.id and t.cat_id=0 and t.id=? and u.username=?;"
rows := database.query(getTaskSQL, id, username, id, username)
2015-11-13 17:04:42 +08:00
defer rows.Close()
2015-11-13 21:11:30 +08:00
if rows.Next() {
err := rows.Scan(&task.Id, &task.Title, &task.Content, &task.Priority, &task.IsHidden, &task.Category)
2015-11-13 21:11:30 +08:00
if err != nil {
log.Println(err)
//send email to respective people
2015-11-13 17:04:42 +08:00
}
}
2015-11-22 10:59:39 +08:00
tasks = append(tasks, task)
context := types.Context{Tasks: tasks, Navigation: "edit"}
2016-02-01 21:45:04 +08:00
return context, nil
2015-11-13 17:04:42 +08:00
}
2015-11-21 14:39:15 +08:00
//TrashTask is used to delete the task
2016-05-14 15:26:24 +08:00
func TrashTask(username string, id int) error {
err := taskQuery("update task set task_status_id=?,last_modified_at=datetime() where user_id=(select id from user where username=?) and id=?", taskStatus["DELETED"], username, id)
2015-11-14 18:56:53 +08:00
return err
}
2015-11-21 14:39:15 +08:00
//CompleteTask is used to mark tasks as complete
2016-05-14 15:26:24 +08:00
func CompleteTask(username string, id int) error {
err := taskQuery("update task set task_status_id=?, finish_date=datetime(),last_modified_at=datetime() where id=? and user_id=(select id from user where username=?) ", taskStatus["COMPLETE"], id, username)
2015-11-13 17:04:42 +08:00
return err
}
2015-11-21 14:39:15 +08:00
//DeleteAll is used to empty the trash
2016-05-14 15:26:24 +08:00
func DeleteAll(username string) error {
err := taskQuery("delete from task where task_status_id=? where user_id=(select id from user where username=?)", taskStatus["DELETED"], username)
2015-11-13 17:04:42 +08:00
return err
}
2015-11-21 14:39:15 +08:00
//RestoreTask is used to restore tasks from the Trash
2016-05-14 15:26:24 +08:00
func RestoreTask(username string, id int) error {
err := taskQuery("update task set task_status_id=?,last_modified_at=datetime(),finish_date=null where id=? and user_id=(select id from user where username=?)", taskStatus["PENDING"], id, username)
return err
}
//RestoreTaskFromComplete is used to restore tasks from the Trash
2016-05-14 15:26:24 +08:00
func RestoreTaskFromComplete(username string, id int) error {
err := taskQuery("update task set finish_date=null,last_modified_at=datetime(), task_status_id=? where id=? and user_id=(select id from user where username=?)", taskStatus["PENDING"], id, username)
2016-01-09 13:03:35 +08:00
return err
}
2015-11-21 14:39:15 +08:00
//DeleteTask is used to delete the task from the database
2016-05-14 15:26:24 +08:00
func DeleteTask(username string, id int) error {
err := taskQuery("delete from task where id = ? and user_id=(select id from user where username=?)", id, username)
2015-11-13 17:04:42 +08:00
return err
}
2015-11-21 14:39:15 +08:00
//AddTask is used to add the task in the database
//TODO: add dueDate feature later
func AddTask(title, content, category string, taskPriority int, username string, hidden int) error {
2016-05-14 15:26:24 +08:00
log.Println("AddTask: started function")
2016-02-03 01:40:44 +08:00
var err error
/*var timeDueDate time.Time
if duedate != "" {
timeDueDate, err = time.Parse("12/31/2016", duedate)
if err != nil {
log.Fatal(err)
}
}*/
2016-05-14 15:26:24 +08:00
userID, err := GetUserID(username)
if err != nil && (title != "" || content != "") {
2016-05-14 15:26:24 +08:00
return err
}
2016-02-03 01:40:44 +08:00
if category == "" {
err = taskQuery("insert into task(title, content, priority, task_status_id, created_date, last_modified_at, user_id,hide) values(?,?,?,?,datetime(), datetime(),?,?)", title, content, taskPriority, taskStatus["PENDING"], userID, hidden)
2016-02-03 01:40:44 +08:00
} else {
2016-05-14 15:26:24 +08:00
categoryID := GetCategoryByName(username, category)
err = taskQuery("insert into task(title, content, priority, created_date, last_modified_at, cat_id, task_status_id, user_id,hide) values(?,?,?,datetime(), datetime(), ?,?,?,?)", title, content, taskPriority, categoryID, taskStatus["PENDING"], userID, hidden)
2016-02-03 01:40:44 +08:00
}
2015-11-13 17:04:42 +08:00
return err
}
2016-05-14 15:26:24 +08:00
//GetCategoryIDByName will return the category ID for the category, used in the edit task
2016-02-06 02:09:09 +08:00
//function where we need to be able to update the categoryID of the task
2016-05-14 15:26:24 +08:00
func GetCategoryIDByName(username string, category string) int {
2016-02-06 02:09:09 +08:00
var categoryID int
2016-05-14 15:26:24 +08:00
getTaskSQL := "select c.id from category c , user u where u.id = c.user_id and name=? and u.username=?"
2016-02-06 02:09:09 +08:00
2016-05-14 15:26:24 +08:00
rows := database.query(getTaskSQL, category, username)
2016-02-06 02:09:09 +08:00
defer rows.Close()
if rows.Next() {
err := rows.Scan(&categoryID)
if err != nil {
log.Println(err)
//send email to respective people
}
}
return categoryID
}
2015-11-21 14:39:15 +08:00
//UpdateTask is used to update the tasks in the database
2016-08-07 18:35:57 +08:00
func UpdateTask(id int, title, content, category string, priority int, username string, hidden int) error {
2016-05-14 15:26:24 +08:00
categoryID := GetCategoryIDByName(username, category)
userID, err := GetUserID(username)
if err != nil {
return err
}
err = taskQuery("update task set title=?, content=?, cat_id=?, priority = ? where id=? and user_id=?", title, content, categoryID, priority, id, userID)
return err
}
2016-01-31 22:22:00 +08:00
//taskQuery encapsulates running multiple queries which don't do much things
func taskQuery(sql string, args ...interface{}) error {
log.Print("inside task query")
2016-01-31 22:10:15 +08:00
SQL := database.prepare(sql)
tx := database.begin()
2016-01-31 22:00:31 +08:00
_, err = tx.Stmt(SQL).Exec(args...)
2015-11-13 21:11:30 +08:00
if err != nil {
2016-05-14 15:26:24 +08:00
log.Println("taskQuery: ", err)
2015-11-13 17:04:42 +08:00
tx.Rollback()
} else {
err = tx.Commit()
if err != nil {
log.Println(err)
return err
}
log.Println("Commit successful")
2015-11-13 17:04:42 +08:00
}
return err
}
2015-11-21 14:39:15 +08:00
//SearchTask is used to return the search results depending on the query
2016-05-14 15:26:24 +08:00
func SearchTask(username, query string) (types.Context, error) {
2016-02-29 10:05:38 +08:00
var tasks []types.Task
var task types.Task
2015-11-21 19:03:34 +08:00
var TaskCreated time.Time
2015-11-21 22:12:20 +08:00
var context types.Context
2015-11-13 17:04:42 +08:00
2016-05-14 15:26:24 +08:00
comments, err := GetComments(username)
2016-02-29 10:05:38 +08:00
if err != nil {
log.Println("SearchTask: something went wrong in finding comments")
}
2016-05-14 15:26:24 +08:00
userID, err := GetUserID(username)
if err != nil {
return context, err
}
stmt := "select t.id, title, content, created_date, priority, c.name from task t, category c where t.user_id=? and c.id = t.cat_id and (title like '%" + query + "%' or content like '%" + query + "%') order by created_date desc"
2016-02-29 10:05:38 +08:00
2016-05-14 15:26:24 +08:00
rows := database.query(stmt, userID, query, query)
defer rows.Close()
2015-11-13 21:11:30 +08:00
for rows.Next() {
2016-02-29 10:05:38 +08:00
err := rows.Scan(&task.Id, &task.Title, &task.Content, &TaskCreated, &task.Priority, &task.Category)
2015-11-13 21:11:30 +08:00
if err != nil {
log.Println(err)
2015-11-13 17:04:42 +08:00
}
2016-02-29 10:05:38 +08:00
if comments[task.Id] != nil {
task.Comments = comments[task.Id]
}
task.Title = strings.Replace(task.Title, query, "<span class='highlight'>"+query+"</span>", -1)
task.Content = strings.Replace(task.Content, query, "<span class='highlight'>"+query+"</span>", -1)
task.Content = string(md.Markdown([]byte(task.Content)))
TaskCreated = TaskCreated.Local()
2016-03-01 02:26:41 +08:00
CurrentTime := time.Now().Local()
week := TaskCreated.AddDate(0, 0, 7)
if (week.String() < CurrentTime.String()) && (task.Priority != "1") {
task.IsOverdue = true // If one week then overdue by default
}
2016-03-01 02:47:14 +08:00
task.Created = TaskCreated.Format("Jan 2 2006")
2016-02-29 10:05:38 +08:00
tasks = append(tasks, task)
2015-11-13 17:04:42 +08:00
}
2016-02-29 10:05:38 +08:00
context = types.Context{Tasks: tasks, Search: query, Navigation: "search"}
2016-05-14 15:26:24 +08:00
return context, nil
2015-11-13 17:04:42 +08:00
}
//GetComments is used to get comments, all of them.
//We do not want 100 different pages to show tasks, we want to use as few pages as possible
//so we are going to populate everything on the damn home pages
2016-05-14 15:26:24 +08:00
func GetComments(username string) (map[int][]types.Comment, error) {
commentMap := make(map[int][]types.Comment)
2016-02-17 00:53:03 +08:00
var taskID int
var comment types.Comment
var created time.Time
2016-05-14 15:26:24 +08:00
userID, err := GetUserID(username)
if err != nil {
return commentMap, err
}
2016-05-15 20:30:19 +08:00
stmt := "select c.id, c.taskID, c.content, c.created, u.username from comments c, task t, user u where t.id=c.taskID and c.user_id=t.user_id and t.user_id=u.id and u.id=?"
2016-05-14 15:26:24 +08:00
rows := database.query(stmt, userID)
defer rows.Close()
for rows.Next() {
2016-05-15 20:30:19 +08:00
err := rows.Scan(&comment.ID, &taskID, &comment.Content, &created, &comment.Username)
2016-06-01 12:18:57 +08:00
comment.Content = string(md.Markdown([]byte(comment.Content)))
if err != nil {
2016-02-17 00:53:03 +08:00
return commentMap, err
}
2016-02-17 00:53:03 +08:00
// comment.Content = string(md.Markdown([]byte(comment.Content))) ## have to fix the <p> issue markdown support
created = created.Local()
2016-03-01 02:47:14 +08:00
comment.Created = created.Format("Jan 2 2006 15:04:05")
2016-02-17 00:53:03 +08:00
commentMap[taskID] = append(commentMap[taskID], comment)
}
2016-02-17 00:53:03 +08:00
return commentMap, nil
}
//AddComments will be used to add comments in the database
2016-05-14 15:26:24 +08:00
func AddComments(username string, id int, comment string) error {
userID, err := GetUserID(username)
if err != nil {
return err
}
stmt := "insert into comments(taskID, content, created, user_id) values (?,?,datetime(),?)"
err = taskQuery(stmt, id, comment, userID)
if err != nil {
return err
}
log.Println("added comment to task ID ", id)
return nil
}