Compare commits

..

12 Commits

Author SHA1 Message Date
bel
39ed9280e1 Merge branch 'main' of https://gitea.inhome.blapointe.com/render/slack-bot-vr into main 2024-04-14 10:02:18 -06:00
bel
ac6bf30042 fun day 2024-04-14 01:16:54 -06:00
bel
4eb2117f21 yagni 2024-04-13 23:51:27 -06:00
bel
6411011e62 drop redundant 2024-04-13 23:50:54 -06:00
bel
444ca5d0ca table complete and linked 2024-04-13 23:49:46 -06:00
bel
bb72ff4bfa OH HEY 2024-04-13 23:35:51 -06:00
bel
e8d52274e7 again verbose 2024-04-13 23:16:20 -06:00
bel
8a67f505ce ok template input a lot more verbose 2024-04-13 22:50:38 -06:00
bel
f4b04e01d3 ok but now server side make template ez 2024-04-13 21:26:17 -06:00
bel
e33d1a6a4b i think it is time to pipe or draw 2024-04-13 19:26:37 -06:00
bel
4ac55e2eea fix naming rows vs columns 2024-04-13 19:18:31 -06:00
bel
b0c9c1cf9e column and row tags ezpz 2024-04-13 19:04:49 -06:00
2 changed files with 218 additions and 13 deletions

121
report.go
View File

@@ -3,7 +3,11 @@ package main
import ( import (
"context" "context"
_ "embed" _ "embed"
"encoding/json"
"errors"
"io" "io"
"slices"
"sort"
"text/template" "text/template"
"time" "time"
) )
@@ -12,7 +16,28 @@ import (
var reportTMPL string var reportTMPL string
func ReportSince(ctx context.Context, w io.Writer, s Storage, t time.Time) error { func ReportSince(ctx context.Context, w io.Writer, s Storage, t time.Time) error {
tmpl, err := template.New("report").Parse(reportTMPL) tmpl := template.New("report").Funcs(map[string]any{
"time": func(foo string, args ...any) (any, error) {
switch foo {
case "Unix":
seconds, _ := args[0].(uint64)
return time.Unix(int64(seconds), 0), nil
case "Time.Format":
t, _ := args[1].(time.Time)
return t.Format(args[0].(string)), nil
}
return nil, errors.New("not impl")
},
"json": func(foo string, args ...any) (any, error) {
switch foo {
case "Marshal":
b, err := json.Marshal(args[0])
return string(b), err
}
return nil, errors.New("not impl")
},
})
tmpl, err := tmpl.Parse(reportTMPL)
if err != nil { if err != nil {
return err return err
} }
@@ -22,26 +47,102 @@ func ReportSince(ctx context.Context, w io.Writer, s Storage, t time.Time) error
return err return err
} }
threads, err := s.ThreadsSince(ctx, t)
if err != nil {
return err
}
eventNames, err := s.EventNamesSince(ctx, t) eventNames, err := s.EventNamesSince(ctx, t)
if err != nil { if err != nil {
return err return err
} }
events, err := s.EventsSince(ctx, t) eventIDs, err := s.EventsSince(ctx, t)
if err != nil { if err != nil {
return err return err
} }
type aThread struct {
Thread string
Messages []Message
First Message
Last Message
}
type anEvent struct {
Event string
Threads []aThread
First Message
Last Message
}
type someEvents struct {
Events []anEvent
}
return tmpl.Execute(w, map[string]any{ return tmpl.Execute(w, map[string]any{
"since": t.Format("2006-01-02"), "since": t.Format("2006-01-02"),
"events": func() someEvents {
events := make([]anEvent, len(eventIDs))
for i, event := range eventIDs {
events[i] = func() anEvent {
threadNames := []string{}
for _, m := range messages {
if m.Event == event {
threadNames = append(threadNames, m.Thread)
}
}
slices.Sort(threadNames)
slices.Compact(threadNames)
threads := make([]aThread, len(threadNames))
for i, thread := range threadNames {
threads[i] = func() aThread {
someMessages := []Message{}
for _, m := range messages {
if m.Thread == thread {
someMessages = append(someMessages, m)
}
}
sort.Slice(someMessages, func(i, j int) bool {
return someMessages[i].TS < someMessages[j].TS
})
return aThread{
Thread: thread,
Messages: someMessages,
First: func() Message {
if len(someMessages) == 0 {
return Message{}
}
return someMessages[0]
}(),
Last: func() Message {
if len(someMessages) == 0 {
return Message{}
}
return someMessages[len(someMessages)-1]
}(),
}
}()
}
sort.Slice(threads, func(i, j int) bool {
return threads[i].First.TS < threads[j].First.TS
})
return anEvent{
Event: event,
Threads: threads,
First: func() Message {
if len(threads) == 0 {
return Message{}
}
return threads[0].First
}(),
Last: func() Message {
if len(threads) == 0 {
return Message{}
}
return threads[len(threads)-1].Last
}(),
}
}()
}
return someEvents{
Events: events,
}
}(),
"messages": messages, "messages": messages,
"threads": threads,
"events": events,
"eventNames": eventNames, "eventNames": eventNames,
}) })
} }

View File

@@ -1,11 +1,115 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<header> <head>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
</header> <script type="module">
<body> import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
</script>
<script>
const allMessages = {{ json "Marshal" .messages }};
console.log(allMessages);
function fillForm() {
const filterableFields = [
"EventName",
"Asset",
];
const fieldsToOptions = {};
filterableFields.map((field) => {fieldsToOptions[field] = {}});
allMessages.map((message) => {
Object.keys(fieldsToOptions).map((field) => {fieldsToOptions[field][message[field]] = true});
});
Object.keys(fieldsToOptions).map((field) => {fieldsToOptions[field] = Object.keys(fieldsToOptions[field])});
document.getElementById("form").innerHTML = Object.keys(fieldsToOptions).map((field) => {
}).join("");
console.log(fieldsToOptions);
`
<select name="filter1" multiple>
<option selected>a</option>
<option selected>b</option>
</select>
`
}
function drawAll() {
const messages = filterMessages(allMessages)
drawEventVolumeByName(messages)
drawEventVolumeByWeekday(messages)
drawEventVolumeByHour(messages)
drawEventVolumeByAsset(messages)
}
function filterMessages(messages) {
}
function drawEventVolumeByName() {}
function drawEventVolumeByWeekday() {}
function drawEventVolumeByHour() {}
function drawEventVolumeByAsset() {}
</script>
<style>
rows {
display: flex;
flex-direction: column;
flex-grow: 1;
}
columns {
display: flex;
flex-direction: row;
flex-grow: 1;
}
rows, columns { border: 1px solid red; }
</style>
</head>
<body onload="fillForm()">
<h1>Report</h1> <h1>Report</h1>
<columns>
<form id="form" style="width: 10em; flex-shrink: 0;" onchange="drawAll()">
</form>
<rows>
<rows>
<rows>
<h2>Event Volume by Name</h2>
<div>DRAW ME</div>
</rows>
<columns>
<rows>
<h3>by Weekday</h3>
<div>DRAW ME</div>
</rows>
<rows>
<h3>by Hour</h3>
<div>DRAW ME</div>
</rows>
</columns>
<rows>
<h3>by Asset</h3>
<div>DRAW ME</div>
</rows>
</rows>
<rows>
<div>
<h2>Events</h2>
<table>
<tr>
<th>TS</th>
<th>Event</th>
<th>EventName</th>
<th>Latest</th>
</tr>
{{ range .events.Events }}
<tr>
<td><a href="{{ .First.Source }}">{{ time "Unix" .First.TS | time "Time.Format" "Mon Jan 02" }}</a></td>
<td><a href="https://TODO">{{ .Event }}</a></td>
<td>{{ .First.EventName }}</td>
<td><a href="{{ .Last.Source }}">{{ .Last.Plaintext }}</a></td>
</tr>
{{ end }}
</table>
</div>
</rows>
</rows>
</columns>
</body> </body>
<footer> <footer>
</footer> </footer>