Get tasks between a date range (#41)

This commit is contained in:
konrad 2018-12-22 18:06:14 +00:00 committed by Gitea
parent 784b890f70
commit 5a93475be9
10 changed files with 356 additions and 52 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
.idea/
.idea/*/*
.idea/httpRequests
config.yml
config.yaml
*.db

View File

@ -255,6 +255,9 @@ Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten.
### Later/Nice to have
* [x] Deps nach mod umziehen
* [x] Start/Enddatum für Tasks
* [x] Timeline/Calendar view -> Dazu tasks die in einem Bestimmten Bereich due sind, macht dann das Frontend
* [x] Tasks innerhalb eines definierbarem Bereich, sollte aber trotzdem der server machen, so à la "Gib mir alles für diesen Monat"
* [ ] Websockets
* Nur lesend? (-> Updates wie bisher)
* sollen den geupdaten Kram an alle anderen user schicken
@ -268,15 +271,12 @@ Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten.
* [ ] Mgl. zum Accountlöschen haben (so richtig krass mit emailverifiezierung und dass alle Privaten Listen gelöscht werden und man alle geteilten entweder wem übertragen muss oder auf privat stellen)
* [ ] IMAP-Integration -> Man schickt eine email an Vikunja und es macht daraus dann nen task -> Achtung missbrauchsmöglichkeiten
* [ ] In und Out webhooks, mit Templates vom Payload
* [x] Start/Enddatum für Tasks
* [ ] Timeline/Calendar view -> Dazu tasks die in einem Bestimmten Bereich due sind, macht dann das Frontend
* [ ] "Smart Lists", Listen nach bestimmten Kriterien gefiltert -> nur UI?
* [ ] "Performance-Statistik" -> Wie viele Tasks man in bestimmten Zeiträumen so geschafft hat etc
* [ ] Activity Feed, so à la "der und der hat das und das gemacht etc"
* [ ] Assignees
* [ ] Attachments
* [ ] Labels
* [ ] Tasks innerhalb eines definierbarem Bereich, sollte aber trotzdem der server machen, so à la "Gib mir alles für diesen Monat"
* [ ] Task-Templates innerhalb namespaces und Listen (-> Mehrere, die auswählbar sind)
* [ ] Bulk-edit -> Transactions
* [ ] Ein Task muss von mehreren Assignees abgehakt werden bis er als done markiert wird

View File

@ -118,6 +118,12 @@ Authorization: Bearer {{auth_token}}
###
# Get all pending tasks in a range
GET http://localhost:8080/api/v1/tasks/all/dueadateasc/1546784000/1548784000
Authorization: Bearer {{auth_token}}
###
# Get all pending tasks in caldav
GET http://localhost:8080/api/v1/tasks/caldav
#Authorization: Bearer {{auth_token}}
@ -125,10 +131,10 @@ GET http://localhost:8080/api/v1/tasks/caldav
###
# Update a task
POST http://localhost:8080/api/v1/tasks/32
POST http://localhost:8080/api/v1/tasks/3491
Authorization: Bearer {{auth_token}}
Content-Type: application/json
{"done":true}
{"startDate":1546804000, "endDate": 1546805000}
###

View File

@ -38,4 +38,26 @@
list_id: 1
created: 1543626724
updated: 1543626724
due_date_unix: 1543616724
due_date_unix: 1543616724
- id: 7
text: 'task #7 with start date'
created_by_id: 1
list_id: 1
created: 1543626724
updated: 1543626724
start_date_unix: 1544600000
- id: 8
text: 'task #8 with end date'
created_by_id: 1
list_id: 1
created: 1543626724
updated: 1543626724
end_date_unix: 1544700000
- id: 9
text: 'task #9 with start and end date'
created_by_id: 1
list_id: 1
created: 1543626724
updated: 1543626724
start_date_unix: 1544600000
end_date_unix: 1544700000

View File

@ -6,7 +6,10 @@
package models
import "code.vikunja.io/web"
import (
"code.vikunja.io/web"
"time"
)
// SortBy declares constants to sort
type SortBy int
@ -73,11 +76,11 @@ func (lt *ListTask) ReadAll(search string, a web.Auth, page int) (interface{}, e
sortby = SortTasksByUnsorted
}
return GetTasksByUser(search, u, page, sortby)
return GetTasksByUser(search, u, page, sortby, time.Unix(lt.StartDateSortUnix, 0), time.Unix(lt.EndDateSortUnix, 0))
}
//GetTasksByUser returns all tasks for a user
func GetTasksByUser(search string, u *User, page int, sortby SortBy) (tasks []*ListTask, err error) {
func GetTasksByUser(search string, u *User, page int, sortby SortBy, startDate time.Time, endDate time.Time) (tasks []*ListTask, err error) {
// Get all lists
lists, err := getRawListsForUser("", u, page)
if err != nil {
@ -103,8 +106,36 @@ func GetTasksByUser(search string, u *User, page int, sortby SortBy) (tasks []*L
}
// Then return all tasks for that lists
if err := x.In("list_id", listIDs).Where("text LIKE ?", "%"+search+"%").OrderBy(orderby).Find(&tasks); err != nil {
return nil, err
if startDate.Unix() != 0 || endDate.Unix() != 0 {
startDateUnix := time.Now().Unix()
if startDate.Unix() != 0 {
startDateUnix = startDate.Unix()
}
endDateUnix := time.Now().Unix()
if endDate.Unix() != 0 {
endDateUnix = endDate.Unix()
}
if err := x.In("list_id", listIDs).
Where("text LIKE ?", "%"+search+"%").
And("((due_date_unix BETWEEN ? AND ?) OR "+
"(start_date_unix BETWEEN ? and ?) OR "+
"(end_date_unix BETWEEN ? and ?))", startDateUnix, endDateUnix, startDateUnix, endDateUnix, startDateUnix, endDateUnix).
And("(parent_task_id = 0 OR parent_task_id IS NULL)").
OrderBy(orderby).
Find(&tasks); err != nil {
return nil, err
}
} else {
if err := x.In("list_id", listIDs).
Where("text LIKE ?", "%"+search+"%").
And("(parent_task_id = 0 OR parent_task_id IS NULL)").
OrderBy(orderby).
Find(&tasks); err != nil {
return nil, err
}
}
return tasks, err

View File

@ -69,6 +69,34 @@ func sortTasksForTesting(by SortBy) (tasks []*ListTask) {
Updated: 1543626724,
DueDateUnix: 1543616724,
},
{
ID: 7,
Text: "task #7 with start date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
StartDateUnix: 1544600000,
},
{
ID: 8,
Text: "task #8 with end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
EndDateUnix: 1544700000,
},
{
ID: 9,
Text: "task #9 with start and end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
StartDateUnix: 1544600000,
EndDateUnix: 1544700000,
},
}
switch by {
@ -95,24 +123,26 @@ func sortTasksForTesting(by SortBy) (tasks []*ListTask) {
func TestListTask_ReadAll(t *testing.T) {
type fields struct {
ID int64
Text string
Description string
Done bool
DueDateUnix int64
RemindersUnix []int64
CreatedByID int64
ListID int64
RepeatAfter int64
ParentTaskID int64
Priority int64
Sorting string
Subtasks []*ListTask
Created int64
Updated int64
CreatedBy User
CRUDable web.CRUDable
Rights web.Rights
ID int64
Text string
Description string
Done bool
DueDateUnix int64
RemindersUnix []int64
CreatedByID int64
ListID int64
RepeatAfter int64
ParentTaskID int64
Priority int64
Sorting string
StartDateSortUnix int64
EndDateSortUnix int64
Subtasks []*ListTask
Created int64
Updated int64
CreatedBy User
CRUDable web.CRUDable
Rights web.Rights
}
type args struct {
search string
@ -160,7 +190,89 @@ func TestListTask_ReadAll(t *testing.T) {
a: &User{ID: 1},
page: 0,
},
want: sortTasksForTesting(SortTasksByPriorityAsc),
want: []*ListTask{
{
ID: 1,
Text: "task #1",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
},
{
ID: 2,
Text: "task #2 done",
Done: true,
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
},
{
ID: 5,
Text: "task #5 higher due date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
DueDateUnix: 1543636724,
},
{
ID: 6,
Text: "task #6 lower due date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
DueDateUnix: 1543616724,
},
{
ID: 7,
Text: "task #7 with start date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
StartDateUnix: 1544600000,
},
{
ID: 8,
Text: "task #8 with end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
EndDateUnix: 1544700000,
},
{
ID: 9,
Text: "task #9 with start and end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
StartDateUnix: 1544600000,
EndDateUnix: 1544700000,
},
{
ID: 4,
Text: "task #4 low prio",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
Priority: 1,
},
{
ID: 3,
Text: "task #3 high prio",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
Priority: 100,
},
},
wantErr: false,
},
{
@ -177,7 +289,7 @@ func TestListTask_ReadAll(t *testing.T) {
wantErr: false,
},
{
name: "ReadAll ListTasks sorted by due date default (desc)",
name: "ReadAll ListTasks sorted by due date default desc",
fields: fields{
Sorting: "dueadate",
},
@ -215,28 +327,131 @@ func TestListTask_ReadAll(t *testing.T) {
want: sortTasksForTesting(SortTasksByDueDateDesc),
wantErr: false,
},
{
name: "ReadAll ListTasks with range",
fields: fields{
StartDateSortUnix: 1544500000,
EndDateSortUnix: 1544600000,
},
args: args{
search: "",
a: &User{ID: 1},
page: 0,
},
want: []*ListTask{
{
ID: 7,
Text: "task #7 with start date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
StartDateUnix: 1544600000,
},
{
ID: 9,
Text: "task #9 with start and end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
StartDateUnix: 1544600000,
EndDateUnix: 1544700000,
},
},
wantErr: false,
},
{
name: "ReadAll ListTasks with range",
fields: fields{
StartDateSortUnix: 1544700000,
EndDateSortUnix: 1545000000,
},
args: args{
search: "",
a: &User{ID: 1},
page: 0,
},
want: []*ListTask{
{
ID: 8,
Text: "task #8 with end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
EndDateUnix: 1544700000,
},
{
ID: 9,
Text: "task #9 with start and end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
StartDateUnix: 1544600000,
EndDateUnix: 1544700000,
},
},
wantErr: false,
},
{
name: "ReadAll ListTasks with range without end date",
fields: fields{
StartDateSortUnix: 1544700000,
},
args: args{
search: "",
a: &User{ID: 1},
page: 0,
},
want: []*ListTask{
{
ID: 8,
Text: "task #8 with end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
EndDateUnix: 1544700000,
},
{
ID: 9,
Text: "task #9 with start and end date",
CreatedByID: 1,
ListID: 1,
Created: 1543626724,
Updated: 1543626724,
StartDateUnix: 1544600000,
EndDateUnix: 1544700000,
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
lt := &ListTask{
ID: tt.fields.ID,
Text: tt.fields.Text,
Description: tt.fields.Description,
Done: tt.fields.Done,
DueDateUnix: tt.fields.DueDateUnix,
RemindersUnix: tt.fields.RemindersUnix,
CreatedByID: tt.fields.CreatedByID,
ListID: tt.fields.ListID,
RepeatAfter: tt.fields.RepeatAfter,
ParentTaskID: tt.fields.ParentTaskID,
Priority: tt.fields.Priority,
Sorting: tt.fields.Sorting,
Subtasks: tt.fields.Subtasks,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CreatedBy: tt.fields.CreatedBy,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
ID: tt.fields.ID,
Text: tt.fields.Text,
Description: tt.fields.Description,
Done: tt.fields.Done,
DueDateUnix: tt.fields.DueDateUnix,
RemindersUnix: tt.fields.RemindersUnix,
CreatedByID: tt.fields.CreatedByID,
ListID: tt.fields.ListID,
RepeatAfter: tt.fields.RepeatAfter,
ParentTaskID: tt.fields.ParentTaskID,
Priority: tt.fields.Priority,
Sorting: tt.fields.Sorting,
StartDateSortUnix: tt.fields.StartDateSortUnix,
EndDateSortUnix: tt.fields.EndDateSortUnix,
Subtasks: tt.fields.Subtasks,
Created: tt.fields.Created,
Updated: tt.fields.Updated,
CreatedBy: tt.fields.CreatedBy,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,
}
got, err := lt.ReadAll(tt.args.search, tt.args.a, tt.args.page)
if (err != nil) != tt.wantErr {
@ -245,6 +460,20 @@ func TestListTask_ReadAll(t *testing.T) {
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ListTask.ReadAll() = %v, want %v", got, tt.want)
/*fmt.Println("Got:")
gotslice := got.([]*ListTask)
for _, g := range gotslice {
fmt.Println(g.Priority, g.Text)
//fmt.Println(g.StartDateUnix)
//fmt.Println(g.EndDateUnix)
}
fmt.Println("Want:")
wantslice := tt.want.([]*ListTask)
for _, w := range wantslice {
fmt.Println(w.Priority, w.Text)
//fmt.Println(w.StartDateUnix)
//fmt.Println(w.EndDateUnix)
}*/
}
})
}

View File

@ -34,10 +34,13 @@ type ListTask struct {
RepeatAfter int64 `xorm:"int(11) INDEX" json:"repeatAfter"`
ParentTaskID int64 `xorm:"int(11) INDEX" json:"parentTaskID"`
Priority int64 `xorm:"int(11)" json:"priority"`
Sorting string `xorm:"-" json:"-" param:"sort"` // Parameter to sort by
StartDateUnix int64 `xorm:"int(11) INDEX" json:"startDate"`
EndDateUnix int64 `xorm:"int(11) INDEX" json:"endDate"`
Sorting string `xorm:"-" json:"-" param:"sort"` // Parameter to sort by
StartDateSortUnix int64 `xorm:"-" json:"-" param:"startdatefilter"`
EndDateSortUnix int64 `xorm:"-" json:"-" param:"enddatefilter"`
Subtasks []*ListTask `xorm:"-" json:"subtasks"`
Created int64 `xorm:"created" json:"created"`

View File

@ -114,7 +114,18 @@ func (i *ListTask) Update() (err error) {
ot.Done = false
}
_, err = x.ID(i.ID).Cols("text", "description", "done", "due_date_unix", "reminders_unix", "repeat_after").Update(ot)
_, err = x.ID(i.ID).
Cols("text",
"description",
"done",
"due_date_unix",
"reminders_unix",
"repeat_after",
"parent_task_id",
"priority",
"start_date_unix",
"end_date_unix").
Update(ot)
*i = ot
return
}

View File

@ -52,7 +52,7 @@ func Caldav(c echo.Context) error {
}
// Get all tasks for that user
tasks, err := models.GetTasksByUser("", &u, -1, models.SortTasksByUnsorted)
tasks, err := models.GetTasksByUser("", &u, -1, models.SortTasksByUnsorted, time.Now(), time.Now().Add(24*356*time.Hour))
if err != nil {
return handler.HandleHTTPError(err, c)
}

View File

@ -218,6 +218,7 @@ func RegisterRoutes(e *echo.Echo) {
a.PUT("/lists/:list", taskHandler.CreateWeb)
a.GET("/tasks/all", taskHandler.ReadAllWeb)
a.GET("/tasks/all/:sort", taskHandler.ReadAllWeb)
a.GET("/tasks/all/:sort/:startdatefilter/:enddatefilter", taskHandler.ReadAllWeb)
a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb)
a.POST("/tasks/:listtask", taskHandler.UpdateWeb)