206 lines
No EOL
4.9 KiB
Go
206 lines
No EOL
4.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"flag"
|
|
"github.com/rs/zerolog/log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Event struct {
|
|
Name string
|
|
EventTimestamp time.Time
|
|
}
|
|
|
|
var eventlog string
|
|
|
|
//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) {
|
|
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
|
|
} |