hivedav

A curlable free/busy scheduler with CalDAV integration
git clone https://git.in0rdr.ch/hivedav.git
Log | Files | Refs | Pull requests | README | LICENSE

commit a46e67ec297cd981ec27577b25041a96a20b7fd0
parent 1c61378186c012a5cac5be6c146fdd36c11f1279
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date:   Sun, 10 Dec 2023 22:19:30 +0100

feat: select year

Diffstat:
MREADME.md | 12+++++-------
Mapp.env.example | 1+
Mconfig/config.go | 2+-
Mmain.go | 5+++--
Mserver.go | 45++++++++++++++++++++++++++-------------------
Mtemplates/index.html | 8++++----
Mtemplates/listcubicles.html | 8++++----
7 files changed, 44 insertions(+), 37 deletions(-)

diff --git a/README.md b/README.md @@ -10,7 +10,7 @@ go run . ## Usage For example, to get the availability in the next week: ``` -curl http://127.0.0.1:3737/week/$(($(date +%W)+1)) +curl http://127.0.0.1:3737/week/$(date +%Y)/$(($(date +%W)+1)) ``` To query the availability in the current week: @@ -50,7 +50,7 @@ The application is configurable through environment variables or the #HIVEDAV_LISTEN_ADDRESS=127.0.0.1 #HIVEDAV_LISTEN_PORT=3737 #HIVEDAV_CALENDAR=0 -#HIVEDAV_HORIZON=0 +#HIVEDAV_HORIZON=1 #HIVEDAV_BOOKING_SUBJ="HiveDAV Booking" #HIVEDAV_BOOKING_REMINDER=15 #HIVEDAV_SMTP_PORT=587 @@ -75,11 +75,9 @@ There is an example config file provided in `./app.env.example`. collection in the ["calendar home set"](https://www.rfc-editor.org/rfc/rfc4791.html#section-6.2.1) which should be used to read availability data -* The `HIVEDAV_HORIZON` is the number of years to show (until 31.12.) from now - in the calendar. 0 means that only the current year is shown. This sets the -limit for recurring events without end time. Setting it to >0 is possible, but -has no effect, since the program only allows to show/book by calendar week of -the current year. +* The `HIVEDAV_HORIZON` (number in years) limits recurring events without end + time. 0 means that only recurring events in the current year are shown. +Unique events are not affected by `HIVEDAV_HORIZON`. * `HIVEDAV_BOOKING_SUBJ` is the subject of the calendar invite for new booking requests * `HIVEDAV_BOOKING_REMINDER` is the reminder time in minutes before the start diff --git a/app.env.example b/app.env.example @@ -3,6 +3,7 @@ #HIVEDAV_HOST=hivedav.example.com #HIVEDAV_CALDAV_URI= #HIVEDAV_CALENDAR=0 +#HIVEDAV_HORIZON=1 #HIVEDAV_CALDAV_USER= #HIVEDAV_CALDAV_PASSWORD= #HIVEDAV_REFRESH_INTERVAL=30 diff --git a/config/config.go b/config/config.go @@ -37,7 +37,7 @@ func (c *Config) LoadConfig(path string) (*Config, error) { viper.SetDefault("HIVEDAV_LISTEN_PORT", 3737) viper.SetDefault("HIVEDAV_LISTEN_ADDRESS", "[::]") viper.SetDefault("HIVEDAV_CALENDAR", 0) - viper.SetDefault("HIVEDAV_HORIZON", 0) + viper.SetDefault("HIVEDAV_HORIZON", 1) viper.SetDefault("HIVEDAV_REFRESH_INTERVAL", 30) viper.SetDefault("HIVEDAV_BOOKING_SUMMARY", "HiveDAV Booking") viper.SetDefault("HIVEDAV_BOOKING_LOCATION", "https://meet.jit.si") diff --git a/main.go b/main.go @@ -30,6 +30,7 @@ func main() { log.Printf("HiveDAV %s, %s 🍯\n", hivedavVersion, conf.HiveDavHost) log.Println("----------------------------------------------------") log.Printf("Cron interval:\t%d minutes\n", conf.RefreshInterval) + log.Printf("Calendar horizon:\t%d year(s)\n", conf.Horizon) log.Printf("CalDAV server:\t%s\n", conf.CaldavUri) log.Printf("CalDAV user:\t%s\n", conf.CaldavUser) @@ -40,9 +41,9 @@ func main() { router := httprouter.New() router.GET("/", server.Index) - router.GET("/week/:week", server.Week) + router.GET("/week/:year/:week", server.Week) router.GET("/list", server.ListCubicles) - router.GET("/list/:week", server.ListCubiclesInWeek) + router.GET("/list/:year/:week", server.ListCubiclesInWeek) router.POST("/book/:dtstart", server.BookCubicle) router.GET("/book/:dtstart", server.CubicleForm) router.ServeFiles("/css/*filepath", http.Dir("./css")) diff --git a/server.go b/server.go @@ -119,9 +119,15 @@ func dayOfISOWeek(weekday int, week int, year int) (timeIt time.Time, weekNow in // check out of bounds week (last week of the year) limit := time.Date(year, 12, 31, 0, 0, 0, 0, time.Local) _, limitWeek := limit.ISOWeek() + if limitWeek == 1 { + // last day of year already in first week of next year + // go back a week (years, months, days) + limit = limit.AddDate(0, 0, -7) + _, limitWeek = limit.ISOWeek() + } if week > limitWeek { - // set week to maximum + // set week to last week of requested year week = limitWeek } @@ -189,20 +195,19 @@ var funcMap = template.FuncMap{ func (s *Server) Week(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { userAgent := req.Header.Get("user-agent") - timeNow := time.Now() - weekday := timeNow.Weekday() - year, week := timeNow.ISOWeek() - - // overwrite with requested week + // overwrite with requested year and week + year, err := strconv.Atoi(ps.ByName("year")) + if err != nil { log.Printf("Cannot serve requested year '%d'\n", year) + http.Error(w, fmt.Sprintf("Cannot serve requested year '%d'\n", year), http.StatusBadRequest) + return + } week, err := strconv.Atoi(ps.ByName("week")) - if err != nil { - log.Printf("Cannot serve requested week '%d'\n", week) + if err != nil { log.Printf("Cannot serve requested week '%d'\n", week) http.Error(w, fmt.Sprintf("Cannot serve requested week '%d'\n", week), http.StatusBadRequest) return } monday, week := dayOfISOWeek(1, week, year) - log.Printf("Current weekday is '%d'\n", weekday) log.Printf("Serving week '%v' of year '%v'\n", week, year) log.Printf("Monday is '%v'\n", monday) @@ -303,9 +308,9 @@ func (s *Server) Week(w http.ResponseWriter, req *http.Request, ps httprouter.Pa if strings.Contains(userAgent, "curl") { w.Header().Set("Content-Type", "text/plain") io.WriteString(w, fmt.Sprintf("Serving week %d of year %d\n", week, year)) - io.WriteString(w, fmt.Sprintf("List cubicle booking commands: 'curl %s/list/%d'\n", s.config.HiveDavHost, week)) - io.WriteString(w, fmt.Sprintf("> Next week: 'curl %s/week/%d'\n", s.config.HiveDavHost, week+1)) - io.WriteString(w, fmt.Sprintf("< Prev week: 'curl %s/week/%d'\n", s.config.HiveDavHost, week-1)) + io.WriteString(w, fmt.Sprintf("List cubicle booking commands: 'curl %s/list/%d/%d'\n", s.config.HiveDavHost, year, week)) + io.WriteString(w, fmt.Sprintf("> Next week: 'curl %s/week/%d/%d'\n", s.config.HiveDavHost, year, week+1)) + io.WriteString(w, fmt.Sprintf("< Prev week: 'curl %s/week/%d/%d'\n", s.config.HiveDavHost, year, week-1)) pt := prettytable.New(termTableData.TableHead) for _, r := range termTableData.Rows { // convert to slice of interface @@ -342,6 +347,7 @@ func (s *Server) Index(w http.ResponseWriter, req *http.Request, _ httprouter.Pa timeNow := time.Now() _, currentWeek := timeNow.ISOWeek() ps := httprouter.Params{ + httprouter.Param{"year", strconv.Itoa(timeNow.Year())}, httprouter.Param{"week", strconv.Itoa(currentWeek)}, } s.Week(w, req, ps) @@ -515,11 +521,13 @@ func (s *Server) ListCubicles(w http.ResponseWriter, req *http.Request, _ httpro func (s *Server) ListCubiclesInWeek(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { userAgent := req.Header.Get("user-agent") - timeNow := time.Now() - weekday := timeNow.Weekday() - year, week := timeNow.ISOWeek() - - // overwrite with requested week + // overwrite with requested year and week + year, err := strconv.Atoi(ps.ByName("year")) + if err != nil { + log.Printf("Cannot serve requested year '%d'\n", year) + http.Error(w, fmt.Sprintf("Cannot serve requested year '%d'\n", year), http.StatusBadRequest) + return + } week, err := strconv.Atoi(ps.ByName("week")) if err != nil { log.Printf("Cannot serve requested week '%d'\n", week) @@ -528,7 +536,6 @@ func (s *Server) ListCubiclesInWeek(w http.ResponseWriter, req *http.Request, ps } monday, week := dayOfISOWeek(1, week, year) - log.Printf("Current weekday is '%d'\n", weekday) log.Printf("Serving week '%v' of year '%v'\n", week, year) log.Printf("Monday is '%v'\n", monday) @@ -584,7 +591,7 @@ func (s *Server) ListCubiclesInWeek(w http.ResponseWriter, req *http.Request, ps if strings.Contains(userAgent, "curl") { w.Header().Set("Content-Type", "text/plain") io.WriteString(w, fmt.Sprintf("Serving week %d of year %d\n", week, year)) - io.WriteString(w, fmt.Sprintf("Back to calendar: 'curl %s/week/%d'\n", s.config.HiveDavHost, week)) + io.WriteString(w, fmt.Sprintf("Back to calendar: 'curl %s/week/%d/%d'\n", s.config.HiveDavHost, year, week)) pt := prettytable.New(tableData.TableHead) for _, r := range tableData.Rows { diff --git a/templates/index.html b/templates/index.html @@ -9,13 +9,13 @@ Serving week <i>{{ .Week }}</i> of year <i>{{ .Year }}</i> </p> <p> - This site is curlable, try <code>`curl {{ .HiveDavHost }}/week/{{ .Week }}`</code> + This site is curlable, try <code>`curl {{ .HiveDavHost }}/week/{{ .Year }}/{{ .Week }}`</code> </p> <nav> <ul> - <li><a href="/list/{{ .Week }}">List cubicles</a></li> - <li><a href="/week/{{ prev .Week }}">Prev week</a></li> - <li><a href="/week/{{ next .Week }}">Next week</a></li> + <li><a href="/list/{{ .Year }}/{{ .Week }}">List cubicles</a></li> + <li><a href="/week/{{ .Year }}/{{ prev .Week }}">Prev week</a></li> + <li><a href="/week/{{ .Year }}/{{ next .Week }}">Next week</a></li> </ul> </nav> <table class="calendar"> diff --git a/templates/listcubicles.html b/templates/listcubicles.html @@ -9,13 +9,13 @@ Serving week <i>{{ .Week }}</i> of year <i>{{ .Year }}</i> </p> <p> - This site is curlable, try <code>`curl {{ .HiveDavHost }}/list/{{ .Week }}`</code> + This site is curlable, try <code>`curl {{ .HiveDavHost }}/list/{{ .Year }}/{{ .Week }}`</code> </p> <nav> <ul> - <li><a href="/week/{{ .Week }}">Show calendar</a></li> - <li><a href="/list/{{ prev .Week }}">Prev week</a></li> - <li><a href="/list/{{ next .Week }}">Next week</a></li> + <li><a href="/week/{{ .Year }}/{{ .Week }}">Show calendar</a></li> + <li><a href="/list/{{ .Year }}/{{ prev .Week }}">Prev week</a></li> + <li><a href="/list/{{ .Year }}/{{ next .Week }}">Next week</a></li> </ul> </nav> <table>