feat:#49 added new ical lib for description formatting

This commit is contained in:
Elmar Kresse
2024-09-24 16:39:23 +02:00
parent 7b70499171
commit c00990dd8e
9 changed files with 299 additions and 122 deletions

View File

@@ -17,121 +17,153 @@
package ical
import (
ics "github.com/arran4/golang-ical"
"htwkalender/ical/model"
"htwkalender/ical/service/functions"
clock "htwkalender/ical/service/functions/time"
"htwkalender/ical/service/names"
"time"
"github.com/jordic/goics"
_ "time/tzdata"
)
// IcalModel local type for EmitICal function
type IcalModel struct {
Events model.Events
Mapping map[string]model.FeedCollection
}
func GenerateIcalFeed(events model.Events, mapping map[string]model.FeedCollection, userAgent string) *ics.Calendar {
// EmitICal implements the interface for goics
func (icalModel IcalModel) EmitICal() goics.Componenter {
internalClock := clock.RealClock{}
c := generateIcalEmit(icalModel, internalClock)
return c
}
cal := ics.NewCalendarFor("HTWK Kalender")
cal.SetMethod(ics.MethodPublish)
cal.SetProductId("-//HTWK Kalender//htwkalender.de//DE")
cal.SetTzid("Europe/Berlin")
cal.SetXWRCalName("HTWK Kalender")
cal.SetXWRTimezone("Europe/Berlin")
cal.SetVersion("2.0")
cal.SetCalscale("GREGORIAN")
vTimeZone := ics.NewTimezone("Europe/Berlin")
vTimeZone.Components = []ics.Component{
&ics.Daylight{
ComponentBase: ics.ComponentBase{
Properties: []ics.IANAProperty{
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyDtstart),
Value: "19700329T020000",
},
},
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyRrule),
Value: "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU",
},
},
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyTzname),
Value: "CEST",
},
},
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyTzoffsetfrom),
Value: "+0100",
},
},
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyTzoffsetto),
Value: "+0200",
},
},
},
},
},
&ics.Standard{
ComponentBase: ics.ComponentBase{
Properties: []ics.IANAProperty{
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyDtstart),
Value: "19701025T030000",
},
},
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyRrule),
Value: "FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU",
},
},
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyTzname),
Value: "CET",
},
},
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyTzoffsetfrom),
Value: "+0200",
},
},
{
BaseProperty: ics.BaseProperty{
IANAToken: string(ics.PropertyTzoffsetto),
Value: "+0100",
},
},
},
},
},
}
cal.AddVTimezone(vTimeZone)
func generateIcalEmit(icalModel IcalModel, internalClock clock.Clock) *goics.Component {
europeTime, _ := time.LoadLocation("Europe/Berlin")
c := goics.NewComponent()
c.SetType("VCALENDAR")
// PRODID is required by the standard
c.AddProperty("PRODID", "-//HTWK Kalender//htwkalender.de//DE")
internalClock := clock.RealClock{}
c.AddProperty("VERSION", "2.0")
c.AddProperty("CALSCALE", "GREGORIAN")
c.AddProperty("TZID", "EUROPE/BERLIN")
c.AddProperty("X-WR-CALNAME", "HTWK Kalender")
c.AddProperty("X-WR-TIMEZONE", "EUROPE/BERLIN")
//add v time zone
icalModel.vtimezone(c)
for _, event := range events {
mapEntry, mappingFound := mapping[event.UUID]
for _, event := range icalModel.Events {
mapEntry, mappingFound := icalModel.Mapping[event.UUID]
s := goics.NewComponent()
s.SetType("VEVENT")
s.AddProperty(goics.FormatDateTime("DTSTAMP", internalClock.Now().Local().In(europeTime)))
// create a unique id for the event by hashing the event start, end, course and name
var eventHash = functions.HashString(time.Time(event.Start).String() + time.Time(event.End).String() + event.Course + event.Name + event.Rooms)
s.AddProperty("UID", eventHash+"@htwkalender.de")
s.AddProperty(goics.FormatDateTime("DTEND", time.Time(event.End).Local().In(europeTime)))
s.AddProperty(goics.FormatDateTime("DTSTART", time.Time(event.Start).Local().In(europeTime)))
icalEvent := ics.NewEvent(eventHash + "@htwkalender.de")
icalEvent.SetDtStampTime(internalClock.Now().Local().In(europeTime))
icalEvent.SetStartAt(time.Time(event.Start).Local().In(europeTime))
icalEvent.SetEndAt(time.Time(event.End).Local().In(europeTime))
if mappingFound {
addPropertyIfNotEmpty(s, "SUMMARY", replaceNameIfUserDefined(&event, mapEntry))
addAlarmIfSpecified(s, event, mapEntry, internalClock)
addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, replaceNameIfUserDefined(&event, mapEntry))
addAlarmIfSpecified(icalEvent, event, mapEntry, internalClock)
} else {
addPropertyIfNotEmpty(s, "SUMMARY", event.Name)
addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertySummary, event.Name)
}
addPropertyIfNotEmpty(s, "DESCRIPTION", generateDescription(event))
addPropertyIfNotEmpty(s, "LOCATION", event.Rooms)
c.AddComponent(s)
generateUserAgentSpecificDescription(icalEvent, event, userAgent)
addPropertyIfNotEmpty(icalEvent, ics.ComponentPropertyLocation, event.Rooms)
cal.AddVEvent(icalEvent)
}
return c
return cal
}
func (icalModel IcalModel) vtimezone(c *goics.Component) {
tz := goics.NewComponent()
tz.SetType("VTIMEZONE")
tz.AddProperty("TZID", "EUROPE/BERLIN")
//add standard time
icalModel.standard(tz)
//add daylight time
icalModel.daylight(tz)
c.AddComponent(tz)
}
func (icalModel IcalModel) standard(tz *goics.Component) {
st := NewHtwkalenderComponent()
st.SetType("STANDARD")
st.AddProperty("DTSTART", "19701025T030000")
st.AddProperty("RRULE", "FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU")
st.AddProperty("TZOFFSETFROM", "+0200")
st.AddProperty("TZOFFSETTO", "+0100")
st.AddProperty("TZNAME", "CET")
tz.AddComponent(st)
}
// create an override for goics component function Write
// to add the RRULE property
func (icalModel IcalModel) daylight(tz *goics.Component) {
dt := NewHtwkalenderComponent()
dt.SetType("DAYLIGHT")
dt.AddProperty("DTSTART", "19700329T020000")
dt.AddProperty("TZOFFSETFROM", "+0100")
dt.AddProperty("TZOFFSETTO", "+0200")
dt.AddProperty("TZNAME", "CEST")
dt.AddProperty("RRULE", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU")
tz.AddComponent(dt)
// AddPropertyIfNotEmpty adds a property to the component if the value is not empty
// or contains only whitespaces
func addPropertyIfNotEmpty(component *ics.VEvent, key ics.ComponentProperty, value string) {
if !functions.OnlyWhitespace(value) {
component.AddProperty(key, value)
}
}
// if reminder is specified in the configuration for this event, an alarm will be added to the event
func addAlarmIfSpecified(s *goics.Component, event model.Event, mapping model.FeedCollection, clock clock.Clock) {
func addAlarmIfSpecified(icalEvent *ics.VEvent, event model.Event, mapping model.FeedCollection, clock clock.Clock) {
// if event.Start > now
// then add alarm
if time.Time(event.Start).Local().After(clock.Now().Local()) && mapping.Reminder {
a := goics.NewComponent()
a.SetType("VALARM")
a.AddProperty("TRIGGER", "-P0DT0H15M0S")
a.AddProperty("ACTION", "DISPLAY")
a.AddProperty("DESCRIPTION", "Next course: "+replaceNameIfUserDefined(&event, mapping)+" in "+event.Rooms)
s.AddComponent(a)
alarm := icalEvent.AddAlarm()
alarm.AddProperty(ics.ComponentPropertyTrigger, "-P0DT0H15M0S")
alarm.AddProperty(ics.ComponentPropertyAction, "DISPLAY")
alarm.AddProperty(ics.ComponentPropertyDescription, "Next course: "+replaceNameIfUserDefined(&event, mapping)+" in "+event.Rooms)
}
}
@@ -144,30 +176,3 @@ func replaceNameIfUserDefined(event *model.Event, mapping model.FeedCollection)
return event.Name
}
// AddPropertyIfNotEmpty adds a property to the component if the value is not empty
// or contains only whitespaces
func addPropertyIfNotEmpty(component *goics.Component, key string, value string) {
if !functions.OnlyWhitespace(value) {
component.AddProperty(key, value)
}
}
func generateDescription(event model.Event) string {
var description string
if !functions.OnlyWhitespace(event.Prof) {
description += "Profs: " + event.Prof + "\n"
}
if !functions.OnlyWhitespace(event.Course) {
description += "Gruppen: " + event.Course + "\n"
}
if !functions.OnlyWhitespace(event.EventType) {
description += "Typ: " + event.EventType + event.Compulsory + "\n"
}
if !functions.OnlyWhitespace(event.Notes) {
description += "Notizen: " + event.Notes + "\n"
}
return description
}