feat: implement caldav search

This commit is contained in:
2022-04-18 12:47:47 +02:00
parent f9e8f4b9c1
commit d7191461eb
57 changed files with 3893 additions and 20 deletions

View File

@@ -1,12 +1,73 @@
package calendar
import (
"fmt"
"github.com/dolanor/caldav-go/caldav"
"github.com/dolanor/caldav-go/caldav/entities"
"github.com/dolanor/caldav-go/icalendar/components"
"log"
"math"
"net/http"
"strings"
"time"
)
type Caldav interface {
QueryEvents(path string, query *entities.CalendarQuery) (events []*components.Event, oerr error)
}
type Calendar struct {
Location *time.Location
Location *time.Location
cdav Caldav
caldavPath string
caldavSummaryPattern string
}
func NewCaldav(caldavUrl, caldavPath string) (Caldav, error) {
// create a reference to your CalDAV-compliant server
server, _ := caldav.NewServer(caldavUrl)
// create a CalDAV client to speak to the server
var client = caldav.NewClient(server, http.DefaultClient)
// start executing requests!
err := client.ValidateServer(caldavPath)
if err != nil {
return nil, fmt.Errorf("bad caldav configuration, unable to validate connexion: %w", err)
}
return client, nil
}
type Option func(calendar *Calendar)
func WithCaldav(cdav Caldav) Option {
return func(calendar *Calendar) {
calendar.cdav = cdav
}
}
func WithCaldavSummaryPattern(caldavSummaryPattern string) Option {
return func(calendar *Calendar) {
calendar.caldavSummaryPattern = caldavSummaryPattern
}
}
func WithCaldavPath(caldavPath string) Option {
return func(calendar *Calendar) {
calendar.caldavPath = caldavPath
}
}
func New(location *time.Location, opts ...Option) *Calendar {
c := &Calendar{
location,
nil,
"",
"",
}
for _, opt := range opts {
opt(c)
}
return c
}
func (cal *Calendar) GetEasterDay(year int) time.Time {
@@ -72,20 +133,24 @@ func (cal *Calendar) GetHolidays(year int) *[]time.Time {
return &joursFeries
}
func (cal *Calendar) GetHolidaysSet(year int) *map[time.Time]bool {
func (cal *Calendar) GetHolidaysSet(year int) map[time.Time]bool {
holidays := cal.GetHolidays(year)
result := make(map[time.Time]bool, len(*holidays))
for _, h := range *holidays {
result[h] = true
}
return &result
return result
}
func(cal *Calendar) IsHoliday(date time.Time) bool{
func (cal *Calendar) IsHoliday(date time.Time) bool {
h := cal.GetHolidaysSet(date.Year())
d := date.In(cal.Location)
day := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, cal.Location)
return (*h)[day]
caldavHolidays, err := cal.IsHolidaysFromCaldav(day)
if err != nil {
log.Printf("unable to check holidays from caldav: %v", err)
}
return h[day] || caldavHolidays
}
func (cal *Calendar) IsWorkingDay(date time.Time) bool {
@@ -96,6 +161,27 @@ func (cal *Calendar) IsWorkingDayToday() bool {
return cal.IsWorkingDay(time.Now())
}
func (cal *Calendar) IsWeekDay(day time.Time) bool{
func (cal *Calendar) IsWeekDay(day time.Time) bool {
return day.Weekday() >= time.Monday && day.Weekday() <= time.Friday
}
func (cal *Calendar) IsHolidaysFromCaldav(day time.Time) (bool, error) {
if cal.cdav == nil {
return false, nil
}
query, err := entities.NewEventRangeQuery(day.UTC(), day.UTC().Add(23*time.Hour+59*time.Minute))
if err != nil {
return false, fmt.Errorf("unable to build events range query: %v", err)
}
events, err := cal.cdav.QueryEvents(cal.caldavPath, query)
if err != nil {
return false, fmt.Errorf("unable list events from caldav: %v", err)
}
for _, evt := range events {
if strings.Contains(evt.Summary, cal.caldavSummaryPattern) {
return true, nil
}
}
return false, nil
}

View File

@@ -1,6 +1,9 @@
package calendar
import (
"github.com/dolanor/caldav-go/caldav/entities"
"github.com/dolanor/caldav-go/icalendar/components"
"github.com/dolanor/caldav-go/icalendar/values"
"testing"
"time"
)
@@ -48,7 +51,7 @@ func TestCalendar_GetHolidays(t *testing.T) {
time.Date(2020, time.December, 25, 0, 0, 0, 0, loc): true,
}
c := Calendar{loc}
c := New(loc)
holidays := c.GetHolidays(2020)
if len(*holidays) != len(expectedHolidays) {
t.Errorf("bad number of holidays, %d but %d are expected", len(*holidays), len(expectedHolidays))
@@ -80,13 +83,13 @@ func TestCalendar_GetHolidaysSet(t *testing.T) {
time.Date(2020, time.December, 25, 0, 0, 0, 0, loc),
}
c := Calendar{loc}
c := New(loc)
holidays := c.GetHolidaysSet(2020)
if len(*holidays) != len(expectedHolidays) {
t.Errorf("bad number of holidays, %d but %d are expected", len(*holidays), len(expectedHolidays))
if len(holidays) != len(expectedHolidays) {
t.Errorf("bad number of holidays, %d but %d are expected", len(holidays), len(expectedHolidays))
}
for _, h := range expectedHolidays {
if !(*holidays)[h] {
if !(holidays)[h] {
t.Errorf("%v is not a holiday", h)
}
}
@@ -112,10 +115,10 @@ func TestCalendar_IsHolidays(t *testing.T) {
time.Date(2020, time.December, 25, 0, 0, 0, 0, loc),
}
c := Calendar{loc}
c := New(loc)
holidays := c.GetHolidaysSet(2020)
if len(*holidays) != len(expectedHolidays) {
t.Errorf("bad number of holidays, %d but %d are expected", len(*holidays), len(expectedHolidays))
if len(holidays) != len(expectedHolidays) {
t.Errorf("bad number of holidays, %d but %d are expected", len(holidays), len(expectedHolidays))
}
for _, h := range expectedHolidays {
if !c.IsHoliday(h) {
@@ -133,7 +136,7 @@ func TestCalendar_IsWorkingDay(t *testing.T) {
t.Errorf("unable to load time location: %v", err)
t.Fail()
}
c := Calendar{loc}
c := New(loc)
if c.IsWorkingDay(time.Date(2019, time.January, 01, 0, 0, 0, 0, loc)) {
t.Error("1st january is not a working day")
@@ -164,3 +167,114 @@ func TestCalendar_IsWorkingDay(t *testing.T) {
t.Error("Sunday should not be a working day")
}
}
type MockCaldav struct {
events []*components.Event
}
func (m *MockCaldav) QueryEvents(_ string, _ *entities.CalendarQuery) ([]*components.Event, error) {
return m.events, nil
}
func TestCalendar_IsHolidaysFromCaldav(t *testing.T) {
loc, err := time.LoadLocation("Europe/Paris")
if err != nil {
t.Errorf("unable to load time location: %v", err)
t.Fail()
}
type fields struct {
Location *time.Location
cdav *MockCaldav
caldavPath string
caldavSummaryPattern string
}
type args struct {
day time.Time
}
tests := []struct {
name string
fields fields
args args
want bool
wantErr bool
}{
{
name: "Holidays in events",
fields: fields{
Location: loc,
cdav: &MockCaldav{
events: []*components.Event{
{
UID: "1",
DateStart: values.NewDateTime(time.Date(2022, time.April, 16, 0, 0, 0, 0, loc)),
DateEnd: values.NewDateTime(time.Date(2022, time.April, 17, 0, 0, 0, 0, loc)),
Summary: "Holidays",
},
},
},
caldavPath: "my_calendar/",
caldavSummaryPattern: "Holidays",
},
args: args{
day: time.Date(2022, time.April, 16, 0, 0, 0, 0, loc),
},
want: true,
wantErr: false,
},
{
name: "Not Holidays in events",
fields: fields{
Location: loc,
cdav: &MockCaldav{
events: []*components.Event{
{
UID: "1",
DateStart: values.NewDateTime(time.Date(2022, time.April, 16, 0, 0, 0, 0, loc)),
DateEnd: values.NewDateTime(time.Date(2022, time.April, 17, 0, 0, 0, 0, loc)),
Summary: "Another event",
},
},
},
caldavPath: "my_calendar/",
caldavSummaryPattern: "Holidays",
},
args: args{
day: time.Date(2022, time.April, 16, 0, 0, 0, 0, loc),
},
want: false,
wantErr: false,
},
{
name: "No events",
fields: fields{
Location: loc,
cdav: &MockCaldav{},
caldavPath: "my_calendar/",
caldavSummaryPattern: "Holidays",
},
args: args{
day: time.Date(2022, time.April, 15, 0, 0, 0, 0, loc),
},
want: false,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cal := New(
loc,
WithCaldav(tt.fields.cdav),
WithCaldavPath(tt.fields.caldavPath),
WithCaldavSummaryPattern(tt.fields.caldavSummaryPattern),
)
got, err := cal.IsHolidaysFromCaldav(tt.args.day)
if (err != nil) != tt.wantErr {
t.Errorf("IsHolidaysFromCaldav() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("IsHolidaysFromCaldav() got = %v, want %v", got, tt.want)
}
})
}
}