Fix #2: ical calendar: take 'cancelled' status into account #4

Closed
Sybren A. Stüvel wants to merge 3 commits from fix/2-handle-cancelled-events into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
3 changed files with 50 additions and 11 deletions

View File

@ -129,7 +129,22 @@ func (cal *RemoteCalendar) LoadNextMeetingTime(ctx context.Context) (time.Time,
var nextStartTime time.Time var nextStartTime time.Time
for eventIndex, event := range events { for eventIndex, event := range events {
uid := event.GetProperty(ical.ComponentPropertyUniqueId)
if uid == nil {
log.Warn("calendar: ignoring entry without UID property")
continue
}
// Skip cancelled meetings.
if isCancelled(event) {
continue
}
dtStart := event.GetProperty(ical.ComponentPropertyDtStart) dtStart := event.GetProperty(ical.ComponentPropertyDtStart)
if dtStart == nil {
log.Warn("calendar: ignoring entry without DTSTART property", "uid", uid.Value)
continue
}
parsedDtStart, err := time.Parse(icalTimeFormat, dtStart.Value) parsedDtStart, err := time.Parse(icalTimeFormat, dtStart.Value)
if err != nil { if err != nil {
log.Warn("calendar: could not parse DTSTART property", "value", dtStart.Value, "cause", err) log.Warn("calendar: could not parse DTSTART property", "value", dtStart.Value, "cause", err)
@ -146,17 +161,22 @@ func (cal *RemoteCalendar) LoadNextMeetingTime(ctx context.Context) (time.Time,
return nextStartTime, nil return nextStartTime, nil
} }
func (cal *RemoteCalendar) MeetingsOnMonth(ctx context.Context, timeInMonth time.Time) []time.Time { type MeetingInfo struct {
StartTime time.Time
IsCancelled bool
}
func (cal *RemoteCalendar) MeetingsOnMonth(ctx context.Context, timeInMonth time.Time) []MeetingInfo {
log := GetBotLog(ctx) log := GetBotLog(ctx)
calendar, err := cal.LoadFromDisk(ctx) calendar, err := cal.LoadFromDisk(ctx)
if err != nil { if err != nil {
log.Warn("could not load calendar from disk, pretending there are no meetings", "cause", err) log.Warn("could not load calendar from disk, pretending there are no meetings", "cause", err)
return []time.Time{} return []MeetingInfo{}
} }
events := calendar.Events() events := calendar.Events()
inMonth := []time.Time{} inMonth := []MeetingInfo{}
year := timeInMonth.Year() year := timeInMonth.Year()
month := timeInMonth.Month() month := timeInMonth.Month()
@ -173,7 +193,12 @@ func (cal *RemoteCalendar) MeetingsOnMonth(ctx context.Context, timeInMonth time
if parsedDtStart.Year() != year || parsedDtStart.Month() != month { if parsedDtStart.Year() != year || parsedDtStart.Month() != month {
continue continue
} }
inMonth = append(inMonth, parsedDtStart.In(location))
meetingInfo := MeetingInfo{
StartTime: parsedDtStart.In(location),
IsCancelled: isCancelled(event),
}
inMonth = append(inMonth, meetingInfo)
} }
return inMonth return inMonth
@ -294,3 +319,11 @@ func fileExists(path string) bool {
return true return true
} }
} }
func isCancelled(event *ical.VEvent) bool {
status := event.GetProperty(ical.ComponentPropertyStatus)
if status == nil {
return false
}
return status.Value == string(ical.ObjectStatusCancelled)
}

View File

@ -23,9 +23,11 @@ var _ EventConsumer = (*CallAndReponse)(nil)
// MeetingTimer provides the next meeting time. // MeetingTimer provides the next meeting time.
type MeetingTimer interface { type MeetingTimer interface {
NextMeetingTime() time.Time NextMeetingTime() time.Time
MeetingsOnMonth(ctx context.Context, timeInMonth time.Time) []time.Time MeetingsOnMonth(ctx context.Context, timeInMonth time.Time) []MeetingInfo
} }
var _ MeetingTimer = (*RemoteCalendar)(nil)
type CallAndResponseCommand interface { type CallAndResponseCommand interface {
CmdTag() string CmdTag() string
CmdDescription() string CmdDescription() string

View File

@ -51,10 +51,10 @@ func (c *CmdCalendar) Handle(ctx context.Context, car *CallAndReponse, evt *even
car.sendReplyInThread(ctx, evt, calendarMD) car.sendReplyInThread(ctx, evt, calendarMD)
} }
func MeetingCalendar(ctx context.Context, meetingTimes []time.Time) string { func MeetingCalendar(ctx context.Context, meetings []MeetingInfo) string {
log := GetBotLog(ctx) log := GetBotLog(ctx)
dateInMonth := meetingTimes[0] dateInMonth := meetings[0].StartTime
firstOfMonth := time.Date(dateInMonth.Year(), dateInMonth.Month(), 1, 0, 0, 0, 0, dateInMonth.Location()) firstOfMonth := time.Date(dateInMonth.Year(), dateInMonth.Month(), 1, 0, 0, 0, 0, dateInMonth.Location())
lastOfMonth := firstOfMonth.AddDate(0, 1, -1) lastOfMonth := firstOfMonth.AddDate(0, 1, -1)
@ -70,10 +70,14 @@ func MeetingCalendar(ctx context.Context, meetingTimes []time.Time) string {
meetingCal[weekIndex][weekdayIndex] = fmt.Sprintf("%2d", dayNum) meetingCal[weekIndex][weekdayIndex] = fmt.Sprintf("%2d", dayNum)
} }
for _, meetingTime := range meetingTimes { for _, meeting := range meetings {
weekIndex, weekdayIndex := calendarIndex(meetingTime, firstOfMonth) weekIndex, weekdayIndex := calendarIndex(meeting.StartTime, firstOfMonth)
// dayNum := strings.TrimSpace(meetingCal[weekIndex][weekdayIndex]) var entry string
entry := "<strong><u>" + meetingTime.Format("15:04") + "</u></strong>" if meeting.IsCancelled {
entry = "❌"
} else {
entry = "<strong>" + meeting.StartTime.Format("15:04") + "</strong>"
}
meetingCal[weekIndex][weekdayIndex] = entry meetingCal[weekIndex][weekdayIndex] = entry
} }