Merge branch 'main' into 15-calendar-preview

# Conflicts:
#	frontend/src/App.vue
#	frontend/src/view/AdditionalModules.vue
This commit is contained in:
masterelmar
2023-11-20 11:37:25 +01:00
59 changed files with 1539 additions and 811 deletions

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="data" uuid="0cac75ec-a154-4b0c-a84d-58425c2050de">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/pb_data/data.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JpaBuddyIdeaProjectConfig">
<option name="renamerInitialized" value="true" />
</component>
</project>

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="ProjectType">
<option name="id" value="jpab" />
</component>
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/backend.iml" filepath="$PROJECT_DIR$/backend.iml" />
</modules>
</component>
</project>

6
backend/.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@@ -3,7 +3,7 @@ info:
title: HTWKalendar API
version: 1.0.1
servers:
- url: https://cal.ekresse.de
- url: https://htwkalendar.de
description: Production server
- url: http://localhost:8090
description: Local server

View File

@@ -82,7 +82,7 @@ func GetSeminarGroupsEventsFromHTML(seminarGroupsLabel []string) []model.Seminar
func splitEventType(events []model.Event) []model.Event {
for i, event := range events {
matched, _ := regexp.Match("^(V|P|S)(w|p)$", []byte(event.EventType))
matched, _ := regexp.Match("^([VPS])([wp])$", []byte(event.EventType))
if matched {
eventType := event.EventType
event.EventType = eventType[0:1]
@@ -136,17 +136,27 @@ func generateUUIDs(events []model.Event, course string) []model.Event {
}
// convertWeeksToDates converts the week and year to a date
// The date is calculated based on the week and the year
// The time is unset and 23:00 is used as default
// Additionally the semester is added to the event
func convertWeeksToDates(events []model.Event, semester string, year string) []model.Event {
var newEvents []model.Event
eventYear, _ := strconv.Atoi(year)
// for each event we need to calculate the start and end date based on the week and the year
for _, event := range events {
eventWeek, _ := strconv.Atoi(event.Week)
eventDay, _ := date.GetDateFromWeekNumber(eventYear, eventWeek, event.Day)
start := replaceTimeForDate(eventDay, event.Start.Time())
end := replaceTimeForDate(eventDay, event.End.Time())
//Check if end is before start
if end.Before(start) {
end = end.AddDate(0, 0, 1)
}
newEvent := event
newEvent.Start, _ = types.ParseDateTime(start.In(time.UTC))
newEvent.End, _ = types.ParseDateTime(end.In(time.UTC))

View File

@@ -1,6 +1,8 @@
package fetch
import (
"fmt"
"github.com/pocketbase/pocketbase/tools/types"
"htwkalender/model"
"reflect"
"testing"
@@ -340,3 +342,155 @@ func Test_replaceTimeInDate(t *testing.T) {
})
}
}
func Test_convertWeeksToDates(t *testing.T) {
type args struct {
events []model.Event
semester string
year string
}
returnDateTime := func(date time.Time) types.DateTime {
dateTime, err := types.ParseDateTime(date)
if err != nil {
fmt.Println(err)
}
return dateTime
}
tests := []struct {
name string
args args
want []model.Event
}{
{
name: "Test Wintertime",
args: args{
events: []model.Event{
{
Week: "1",
Day: "Montag",
Start: returnDateTime(time.Date(0, 0, 0, 7, 30, 0, 0, time.UTC)),
End: returnDateTime(time.Date(0, 0, 0, 9, 0, 0, 0, time.UTC)),
},
},
semester: "ws",
year: "2021",
},
want: []model.Event{
{
Week: "1",
Day: "Montag",
Start: returnDateTime(time.Date(2021, 1, 4, 6, 30, 0, 0, time.UTC)),
End: returnDateTime(time.Date(2021, 1, 4, 8, 0, 0, 0, time.UTC)),
Semester: "ws",
},
},
},
{
name: "Test Summertime",
args: args{
events: []model.Event{
{
Week: "30",
Day: "Donnerstag",
Start: returnDateTime(time.Date(0, 0, 0, 7, 30, 0, 0, time.UTC)),
End: returnDateTime(time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC)),
},
},
semester: "ws",
year: "2023",
},
want: []model.Event{
{
Week: "30",
Day: "Donnerstag",
Start: returnDateTime(time.Date(2023, 7, 27, 5, 30, 0, 0, time.UTC)),
End: returnDateTime(time.Date(2023, 7, 27, 22, 0, 0, 0, time.UTC)),
Semester: "ws",
},
},
},
{
name: "Test NextDay",
args: args{
events: []model.Event{
{
Week: "45",
Day: "Donnerstag",
Start: returnDateTime(time.Date(0, 0, 0, 7, 30, 0, 0, time.UTC)),
End: returnDateTime(time.Date(0, 0, 0, 4, 0, 0, 0, time.UTC)),
},
},
semester: "ws",
year: "2023",
},
want: []model.Event{
{
Week: "45",
Day: "Donnerstag",
Start: returnDateTime(time.Date(2023, 11, 9, 6, 30, 0, 0, time.UTC)),
End: returnDateTime(time.Date(2023, 11, 10, 3, 0, 0, 0, time.UTC)),
Semester: "ws",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := convertWeeksToDates(tt.args.events, tt.args.semester, tt.args.year); !reflect.DeepEqual(got, tt.want) {
t.Errorf("convertWeeksToDates() = %v, want %v", got, tt.want)
}
})
}
}
func Test_replaceTimeForDate(t *testing.T) {
type args struct {
date time.Time
replacementTime time.Time
}
tests := []struct {
name string
args args
want time.Time
}{
{
name: "Replace Hour",
args: args{
date: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
replacementTime: time.Date(0, 0, 0, 8, 0, 0, 0, time.UTC),
},
want: time.Date(2021, 1, 1, 8, 0, 0, 0, time.UTC),
},
{
name: "Replace Hour and Minute",
args: args{
date: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
replacementTime: time.Date(0, 0, 0, 8, 15, 0, 0, time.UTC),
},
want: time.Date(2021, 1, 1, 8, 15, 0, 0, time.UTC),
},
{
name: "Replace Hour and Minute",
args: args{
date: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
replacementTime: time.Date(0, 0, 0, 8, 30, 0, 0, time.UTC),
},
want: time.Date(2021, 1, 1, 8, 30, 0, 0, time.UTC),
},
{
name: "Replace Hour and Minute without Year, Month, Day",
args: args{
date: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
replacementTime: time.Date(2023, 10, 3, 8, 30, 0, 0, time.UTC),
},
want: time.Date(2021, 1, 1, 8, 30, 0, 0, time.UTC),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := replaceTimeForDate(tt.args.date, tt.args.replacementTime); !reflect.DeepEqual(got, tt.want) {
t.Errorf("replaceTimeForDate() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -4,6 +4,7 @@ import (
"htwkalender/model"
"htwkalender/service/functions"
"htwkalender/service/names"
"strconv"
"time"
"github.com/jordic/goics"
@@ -20,21 +21,29 @@ func (icalModel IcalModel) EmitICal() goics.Componenter {
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")
c.AddProperty("VERSION", "2.0")
c.AddProperty("CALSCAL", "GREGORIAN")
c.AddProperty("TZID", "Europe/Berlin")
c.AddProperty("CALSCALE", "GREGORIAN")
c.AddProperty("TZID", "EUROPE/BERLIN")
c.AddProperty("X-WR-CALNAME", "HTWK Kalender")
c.AddProperty("X-WR-TIMEZONE", "Europe/Berlin")
c.AddProperty("X-LIC-LOCATION", "Europe/Berlin")
for _, event := range icalModel.Events {
c.AddProperty("X-WR-TIMEZONE", "EUROPE/BERLIN")
//add v time zone
icalModel.vtimezone(c)
var timeStamp = time.Now().Local().In(europeTime).Format("20060102T150405")
for i, event := range icalModel.Events {
mapEntry, mappingFound := icalModel.Mapping[event.UUID]
s := goics.NewComponent()
s.SetType("VEVENT")
k, v := goics.FormatDateTime("DTEND;TZID=Europe/Berlin", event.End.Time().Local().In(europeTime))
s.AddProperty(k, v)
k, v = goics.FormatDateTime("DTSTART;TZID=Europe/Berlin", event.Start.Time().Local().In(europeTime))
s.AddProperty(k, v)
s.AddProperty(goics.FormatDateTime("DTSTAMP", time.Now().Local().In(europeTime)))
s.AddProperty("UID", strconv.FormatInt(int64(i), 16)+"-"+timeStamp+"@htwkalender.de")
s.AddProperty(goics.FormatDateTime("DTEND", event.End.Time().Local().In(europeTime)))
s.AddProperty(goics.FormatDateTime("DTSTART", event.Start.Time().Local().In(europeTime)))
if mappingFound {
addPropertyIfNotEmpty(s, "SUMMARY", replaceNameIfUserDefined(&event, mapEntry))
@@ -50,6 +59,43 @@ func (icalModel IcalModel) EmitICal() goics.Componenter {
return c
}
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)
}
// 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) {
if mapping.Reminder {

View File

@@ -0,0 +1,46 @@
package ical
import (
"github.com/jordic/goics"
"sort"
"strings"
)
type HtwkalenderComponent struct {
*goics.Component
}
func NewHtwkalenderComponent() *HtwkalenderComponent {
return &HtwkalenderComponent{
Component: goics.NewComponent(),
}
}
// Writes the component to the Writer
func (c *HtwkalenderComponent) Write(w *goics.ICalEncode) {
w.WriteLine("BEGIN:" + c.Tipo + goics.CRLF)
// Iterate over component properties
var keys []string
for k := range c.Properties {
keys = append(keys, k)
}
sort.Strings(keys)
for _, key := range keys {
vals := c.Properties[key]
for _, val := range vals {
w.WriteLine(WriteStringField(key, val))
}
}
for _, xc := range c.Elements {
xc.Write(w)
}
w.WriteLine("END:" + c.Tipo + goics.CRLF)
}
// WriteStringField UID:asdfasdfаs@dfasdf.com
func WriteStringField(key string, val string) string {
return strings.ToUpper(key) + ":" + (val) + goics.CRLF
}