257 lines
No EOL
6 KiB
Go
257 lines
No EOL
6 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
_ "embed"
|
|
"encoding/json"
|
|
"flag"
|
|
"github.com/rs/zerolog/log"
|
|
"net/http"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
)
|
|
|
|
type Event struct {
|
|
Name string
|
|
EventTimestamp time.Time
|
|
}
|
|
|
|
var eventlog string
|
|
|
|
//go:embed webpage.tmpl
|
|
var templateContent []byte
|
|
|
|
//Current State
|
|
var lastEvent Event
|
|
var currentState map[string]map[string]int64 //Per Name per Day in Secounds
|
|
|
|
|
|
func main() {
|
|
eventlogPath := flag.String("eventlog", "/var/ett/eventlog", "Where to store the eventlog file")
|
|
|
|
flag.Parse()
|
|
eventlog = *eventlogPath
|
|
|
|
log.Info().Msg("Start Time Tracking Service")
|
|
currentState = make(map[string]map[string]int64)
|
|
|
|
defer func() {
|
|
e := Event{
|
|
Name: "Nothing",
|
|
EventTimestamp: time.Now(),
|
|
}
|
|
addEvent(e)
|
|
}()
|
|
|
|
readStateFromFile()
|
|
|
|
runHttpServer()
|
|
}
|
|
|
|
func runHttpServer() {
|
|
http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
|
|
|
|
var taglist []string
|
|
for s, _ := range currentState {
|
|
taglist = append(taglist, s)
|
|
}
|
|
|
|
sort.Strings(taglist)
|
|
|
|
var daylist []string
|
|
for i := 0; i < 7; i++ {
|
|
g := -1 * 24 * i
|
|
t := time.Now().Add(time.Duration(g) * time.Hour)
|
|
daylist = append(daylist, t.Format("02.01.06"))
|
|
}
|
|
|
|
fm := template.FuncMap{"tominute": func(a int64) int64 {
|
|
return a / 60
|
|
}}
|
|
|
|
week := map[string]interface{}{}
|
|
for tag, _ := range currentState {
|
|
var gi int64
|
|
for i := 0; i < 7; i++ {
|
|
g := -1 * 24 * i
|
|
t := time.Now().Add(time.Duration(g) * time.Hour)
|
|
secounds := currentState[tag][t.Format("02.01.06")]
|
|
gi = gi + secounds
|
|
}
|
|
gi = gi / 60
|
|
s := strconv.FormatInt(gi, 10)
|
|
week[tag] = s
|
|
}
|
|
|
|
|
|
templ := template.Must(template.New("page").Funcs(fm).Parse(string(templateContent)))
|
|
templ.Execute(w, map[string]interface{}{
|
|
"lastEvent": lastEvent,
|
|
"taglist": taglist,
|
|
"daylist": daylist,
|
|
"currentState": currentState,
|
|
"weeklist": week,
|
|
})
|
|
|
|
|
|
/*w.Write([]byte("<h1>Time Tracking</h1>"))
|
|
w.Write([]byte("Doing "+lastEvent.Name+" since "+lastEvent.EventTimestamp.Format(time.RFC3339)+"<br>"))
|
|
w.Write([]byte("<h3>Today</h3>"))
|
|
for tag, _ := range currentState {
|
|
i := currentState[tag][time.Now().Format("02.01.06")]
|
|
i = i / 60
|
|
s := strconv.FormatInt(i, 10)
|
|
line := tag+": "+s+"<br>"
|
|
w.Write([]byte(line))
|
|
}
|
|
w.Write([]byte("<h3>Last 7 Days</h3>"))
|
|
for tag, _ := range currentState {
|
|
var gi int64
|
|
for i := 0; i <= 7; i++ {
|
|
g := -1 * 24 * i
|
|
t := time.Now().Add(time.Duration(g) * time.Hour)
|
|
secounds := currentState[tag][t.Format("02.01.06")]
|
|
gi = gi + secounds
|
|
}
|
|
gi = gi / 60
|
|
s := strconv.FormatInt(gi, 10)
|
|
line := tag+": "+s+"<br>"
|
|
w.Write([]byte(line))
|
|
}
|
|
*/
|
|
})
|
|
http.HandleFunc("/current", func(w http.ResponseWriter, r *http.Request) {
|
|
res, err := json.Marshal(lastEvent)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Cant Marshal Current State")
|
|
}
|
|
w.Write(res)
|
|
})
|
|
|
|
|
|
http.HandleFunc("/tags", func(w http.ResponseWriter, r *http.Request) {
|
|
var tags []string
|
|
for s, _ := range currentState {
|
|
tags = append(tags, s)
|
|
}
|
|
res, err := json.Marshal(tags)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Cant Marshal Tag list")
|
|
}
|
|
w.Write(res)
|
|
})
|
|
|
|
http.HandleFunc("/state", func(w http.ResponseWriter, r *http.Request) {
|
|
res, err := json.Marshal(currentState)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Cant Marshal Tag list")
|
|
}
|
|
w.Write(res)
|
|
})
|
|
|
|
http.HandleFunc("/event", func(w http.ResponseWriter, r *http.Request) {
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
log.Warn().Err(err).Msg("Cant pars form Data from Request to add event")
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write([]byte("Cant parse form Data"))
|
|
return
|
|
}
|
|
d := r.FormValue("time")
|
|
eventTimestamp := time.Now()
|
|
if d != "" {
|
|
eventTimestamp, err = time.Parse(time.RFC3339 , d)
|
|
if err != nil {
|
|
log.Warn().Err(err).Str("TimeString", d).Msg("Cant parse time from http get parameter")
|
|
}
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write([]byte("Cant parse date from get paremter, please use RFC3339 Data"))
|
|
return
|
|
}
|
|
|
|
|
|
name := r.FormValue("name")
|
|
|
|
e := Event{
|
|
Name: name,
|
|
EventTimestamp: eventTimestamp,
|
|
}
|
|
|
|
addEvent(e)
|
|
})
|
|
|
|
http.ListenAndServe(":8080", nil)
|
|
}
|
|
|
|
func readStateFromFile() {
|
|
if _, err := os.Stat(eventlog); err == nil {
|
|
// path/to/whatever exists
|
|
file, err := os.Open(eventlog)
|
|
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Cant open file")
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
scanner.Split(bufio.ScanLines)
|
|
|
|
for scanner.Scan() {
|
|
details := strings.Split(scanner.Text(), ";")
|
|
t, err := time.Parse(time.RFC3339 , details[0])
|
|
if err != nil {
|
|
log.Fatal().Err(err).Str("TimeString", details[0]).Msg("Cant parse time from eventlog")
|
|
}
|
|
e := Event{
|
|
Name: details[1],
|
|
EventTimestamp: t,
|
|
}
|
|
|
|
handleEvent(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
func saveEvent(e Event) {
|
|
var line string
|
|
line = e.EventTimestamp.Format(time.RFC3339)+";"+e.Name +"\r\n"
|
|
f, err := os.OpenFile(eventlog, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
if _, err = f.WriteString(line); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
}
|
|
|
|
func addEvent(e Event) {
|
|
//Write Event to Log
|
|
saveEvent(e)
|
|
handleEvent(e)
|
|
}
|
|
|
|
func handleEvent(e Event) {
|
|
log.Debug().Msg("Handle New Event")
|
|
if lastEvent.Name != "" {
|
|
duration := e.EventTimestamp.Sub(lastEvent.EventTimestamp)
|
|
log.Debug().Int64("Duration", int64(duration.Seconds())).Str("LastEventName", lastEvent.Name).Msg("Calculatet Duration for last event")
|
|
//Add to state
|
|
currentDuration := currentState[lastEvent.Name][lastEvent.EventTimestamp.Format("02.01.06")]
|
|
if len(currentState[lastEvent.Name]) == 0 {
|
|
currentState[lastEvent.Name] = make(map[string]int64)
|
|
}
|
|
currentDuration += int64(duration.Seconds())
|
|
log.Debug().Int64("Day Duration", currentDuration).Str("Day", lastEvent.EventTimestamp.Format("02.01.06")).Str("typ", lastEvent.Name).Msg("Add Duration to Day")
|
|
currentState[lastEvent.Name][lastEvent.EventTimestamp.Format("02.01.06")] = currentDuration
|
|
}
|
|
|
|
lastEvent = e
|
|
} |