- commit
- 828bab7
- parent
- 3764a26
- author
- Eric Bower
- date
- 2024-06-16 16:02:50 +0000 UTC
feat(pgs): cli cmd `stats {project}` for project analytics
2 files changed,
+77,
-1
+72,
-0
1@@ -6,6 +6,7 @@ import (
2 "log/slog"
3 "path/filepath"
4 "strings"
5+ "time"
6
7 "github.com/charmbracelet/lipgloss"
8 "github.com/charmbracelet/lipgloss/table"
9@@ -63,6 +64,10 @@ func getHelpText(styles common.Styles, userName string, width int) string {
10 "stats",
11 "usage statistics",
12 },
13+ {
14+ fmt.Sprintf("stats %s", projectName),
15+ fmt.Sprintf("site analytics for `%s`", projectName),
16+ },
17 {
18 "ls",
19 "lists projects",
20@@ -191,6 +196,73 @@ func (c *Cmd) help() {
21 c.output(getHelpText(c.Styles, c.User.Name, c.Width))
22 }
23
24+func uniqueVisitorsTbl(intervals []*db.VisitInterval) *table.Table {
25+ headers := []string{"Date", "Unique Visitors"}
26+ data := [][]string{}
27+ for _, d := range intervals {
28+ data = append(data, []string{
29+ d.Interval.Format(time.RFC3339Nano),
30+ fmt.Sprintf("%d", d.Visitors),
31+ })
32+ }
33+
34+ t := table.New().
35+ Border(lipgloss.RoundedBorder()).
36+ Headers(headers...).
37+ Rows(data...)
38+ return t
39+}
40+
41+func visitUrlsTbl(urls []*db.VisitUrl) *table.Table {
42+ headers := []string{"Site", "Count"}
43+ data := [][]string{}
44+ for _, d := range urls {
45+ data = append(data, []string{
46+ d.Url,
47+ fmt.Sprintf("%d", d.Count),
48+ })
49+ }
50+
51+ t := table.New().
52+ Border(lipgloss.RoundedBorder()).
53+ Headers(headers...).
54+ Rows(data...)
55+ return t
56+}
57+
58+func (c *Cmd) statsByProject(projectName string) error {
59+ project, err := c.Dbpool.FindProjectByName(c.User.ID, projectName)
60+ if err != nil {
61+ return errors.Join(err, fmt.Errorf("project (%s) does not exit", projectName))
62+ }
63+
64+ opts := &db.SummaryOpts{
65+ FkID: project.ID,
66+ By: "project_id",
67+ Interval: "day",
68+ Origin: shared.StartOfMonth(),
69+ }
70+
71+ summary, err := c.Dbpool.VisitSummary(opts)
72+ if err != nil {
73+ return err
74+ }
75+
76+ c.output("Top URLs")
77+ topUrlsTbl := visitUrlsTbl(summary.TopUrls)
78+ c.output(topUrlsTbl.Width(c.Width).String())
79+
80+ c.output("Top Referers")
81+ topRefsTbl := visitUrlsTbl(summary.TopReferers)
82+ c.output(topRefsTbl.Width(c.Width).String())
83+
84+ uniqueTbl := uniqueVisitorsTbl(summary.Intervals)
85+ c.output("Unique Visitors this Month")
86+ c.output(uniqueTbl.Width(c.Width).String())
87+
88+ return nil
89+}
90+
91 func (c *Cmd) stats(cfgMaxSize uint64) error {
92 ff, err := c.Dbpool.FindFeatureForUser(c.User.ID, "plus")
93 if err != nil {
+5,
-1
1@@ -136,7 +136,11 @@ func WishMiddleware(handler *uploadassets.UploadAssetHandler) wish.Middleware {
2 "cmdArgs", cmdArgs,
3 )
4
5- if cmd == "link" {
6+ if cmd == "stats" {
7+ err := opts.statsByProject(projectName)
8+ opts.bail(err)
9+ return
10+ } else if cmd == "link" {
11 linkCmd, write := flagSet("link", sesh)
12 linkTo := linkCmd.String("to", "", "symbolic link to this project")
13 if !flagCheck(linkCmd, projectName, cmdArgs) {