Skip to content

Commit fdb9a0a

Browse files
author
Working On It
committed
feat: add activity page (casibase#1383)
1 parent 33b17ec commit fdb9a0a

File tree

16 files changed

+775
-0
lines changed

16 files changed

+775
-0
lines changed

controllers/activity.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2025 The Casibase Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package controllers
16+
17+
import (
18+
"github.com/casibase/casibase/object"
19+
"github.com/casibase/casibase/util"
20+
)
21+
22+
// GetActivities
23+
// @Title GetActivities
24+
// @Tag Activity API
25+
// @Description get activities
26+
// @Param days query string true "days count"
27+
// @Success 200 {array} object.Activity The Response object
28+
// @router /get-activities [get]
29+
func (c *ApiController) GetActivities() {
30+
days := util.ParseInt(c.Input().Get("days"))
31+
user := c.Input().Get("selectedUser")
32+
field := c.Input().Get("field")
33+
34+
activities, err := object.GetActivities(days, user, field)
35+
if err != nil {
36+
c.ResponseError(err.Error())
37+
return
38+
}
39+
40+
c.ResponseOk(activities)
41+
}
42+
43+
// GetRangeActivities
44+
// @Title GetRangeActivities
45+
// @Tag Activity API
46+
// @Description get range activities
47+
// @Param count query string true "count of range activities"
48+
// @Success 200 {array} object.Activity The Response object
49+
// @router /get-range-activities [get]
50+
func (c *ApiController) GetRangeActivities() {
51+
rangeType := c.Input().Get("rangeType")
52+
count := util.ParseInt(c.Input().Get("count"))
53+
user := c.Input().Get("user")
54+
field := c.Input().Get("field")
55+
56+
activities, err := object.GetRangeActivities(rangeType, count, user, field)
57+
if err != nil {
58+
c.ResponseError(err.Error())
59+
return
60+
}
61+
62+
c.ResponseOk(activities)
63+
}

object/activity.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Copyright 2024 The Casibase Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package object
16+
17+
import (
18+
"encoding/json"
19+
"errors"
20+
"fmt"
21+
"time"
22+
)
23+
24+
type Activity struct {
25+
Date string `json:"date"`
26+
FieldCount map[string]int
27+
}
28+
29+
func getTargetfieldValue(record *Record, fieldName string) (string, error) {
30+
switch fieldName {
31+
case "action":
32+
return record.Action, nil
33+
case "language":
34+
return record.Language, nil
35+
case "client_ip":
36+
return record.ClientIp, nil
37+
case "owner":
38+
return record.Owner, nil
39+
case "organization":
40+
return record.Organization, nil
41+
case "user_agent":
42+
return record.UserAgent, nil
43+
case "response":
44+
var data map[string]interface{}
45+
err := json.Unmarshal([]byte(record.Response), &data)
46+
if err != nil {
47+
return "", err
48+
}
49+
status, ok := data["status"].(string)
50+
if ok {
51+
return status, nil
52+
} else {
53+
return "", err
54+
}
55+
}
56+
return "", errors.New("no matched field")
57+
}
58+
59+
func GetActivities(days int, user string, fieldName string) ([]*Activity, error) {
60+
records := []*Record{}
61+
err := adapter.engine.Desc("created_time").Find(&records, &Record{Owner: "casbin"})
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
now := time.Now().UTC()
67+
// Adjusted to include today in the count by subtracting days-1
68+
startDateTime := now.AddDate(0, 0, -(days - 1)).Truncate(24 * time.Hour)
69+
70+
// Adjusted the size to days, as we're now including today
71+
activities := make([]*Activity, days)
72+
73+
for i := 0; i < days; i++ {
74+
activities[i] = &Activity{
75+
Date: startDateTime.AddDate(0, 0, i).Format("2006-01-02"),
76+
FieldCount: make(map[string]int),
77+
}
78+
}
79+
80+
for _, record := range records {
81+
if !(record.User == user || user == "All") {
82+
continue
83+
}
84+
recordTime, _ := time.Parse(time.RFC3339, record.CreatedTime)
85+
86+
if recordTime.Before(startDateTime) {
87+
break
88+
}
89+
90+
// Find the date index for the message
91+
dayIndex := int(recordTime.Sub(startDateTime).Hours() / 24)
92+
if dayIndex < 0 || dayIndex >= days {
93+
continue
94+
}
95+
value, err := getTargetfieldValue(record, fieldName)
96+
if err != nil {
97+
return nil, err
98+
}
99+
activities[dayIndex].FieldCount[value] += 1
100+
}
101+
102+
for i := 1; i < days; i++ {
103+
for action, count := range activities[i-1].FieldCount {
104+
activities[i].FieldCount[action] += count
105+
}
106+
}
107+
108+
return activities, nil
109+
}
110+
111+
func GetRangeActivities(rangeType string, count int, user string, fieldName string) ([]*Activity, error) {
112+
records, err := GetRecords("casbin")
113+
if err != nil {
114+
return nil, err
115+
}
116+
117+
now := time.Now()
118+
119+
var startDateTime time.Time
120+
121+
switch rangeType {
122+
case "Hour":
123+
startDateTime = now.Truncate(time.Hour).Add(-time.Hour * time.Duration(count-1))
124+
case "Day":
125+
startDateTime = now.Truncate(24*time.Hour).AddDate(0, 0, -(count - 1))
126+
case "Week":
127+
offset := int(time.Monday - now.Weekday())
128+
if offset > 0 {
129+
offset -= 7
130+
}
131+
startOfWeek := now.AddDate(0, 0, offset)
132+
startDateTime = startOfWeek.Truncate(24*time.Hour).AddDate(0, 0, -7*(count-1))
133+
case "Month":
134+
startDateTime = now.Truncate(24*time.Hour).AddDate(0, -count+1, 0)
135+
default:
136+
return nil, fmt.Errorf("invalid range type: %s", rangeType)
137+
}
138+
139+
activities := make([]*Activity, count)
140+
for i := range activities {
141+
activities[i] = &Activity{FieldCount: make(map[string]int)}
142+
}
143+
144+
for _, record := range records {
145+
if !(user == "All" || record.User == user) {
146+
continue
147+
}
148+
recordTime, _ := time.Parse(time.RFC3339, record.CreatedTime)
149+
bucketIndex := -1
150+
151+
switch rangeType {
152+
case "Hour":
153+
bucketIndex = int(recordTime.Sub(startDateTime).Hours())
154+
case "Day":
155+
bucketIndex = int(recordTime.Sub(startDateTime).Hours() / 24)
156+
case "Week":
157+
bucketIndex = int(recordTime.Sub(startDateTime).Hours() / (24 * 7))
158+
case "Month":
159+
monthDiff := (recordTime.Year()-startDateTime.Year())*12 + int(recordTime.Month()-startDateTime.Month())
160+
bucketIndex = monthDiff
161+
}
162+
163+
if bucketIndex >= 0 && bucketIndex < count {
164+
value, err := getTargetfieldValue(record, fieldName)
165+
if err != nil {
166+
return nil, err
167+
}
168+
activities[bucketIndex].FieldCount[value] += 1
169+
}
170+
}
171+
172+
// Assign dates and refine price for each usage after calculations are complete
173+
for i, activity := range activities {
174+
var dateLabel string
175+
switch rangeType {
176+
case "Hour":
177+
dateLabel = startDateTime.Add(time.Hour * time.Duration(i)).Format("2006-01-02 15")
178+
case "Day":
179+
dateLabel = startDateTime.AddDate(0, 0, i).Format("2006-01-02")
180+
case "Week":
181+
dateLabel = startDateTime.AddDate(0, 0, 7*i).Format("2006-01-02")
182+
case "Month":
183+
dateLabel = startDateTime.AddDate(0, i, 0).Format("2006-01")
184+
}
185+
activity.Date = dateLabel
186+
}
187+
188+
return activities, nil
189+
}

routers/router.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ func initAPI() {
115115
beego.Router("/api/get-users", &controllers.ApiController{}, "GET:GetUsers")
116116
beego.Router("/api/get-user-table-infos", &controllers.ApiController{}, "GET:GetUserTableInfos")
117117

118+
beego.Router("/api/get-activities", &controllers.ApiController{}, "GET:GetActivities")
119+
// beego.Router("/api/get-range-activities", &controllers.ApiController{}, "GET:GetRangeActivities")
120+
118121
beego.Router("/api/get-global-workflows", &controllers.ApiController{}, "GET:GetGlobalWorkflows")
119122
beego.Router("/api/get-workflows", &controllers.ApiController{}, "GET:GetWorkflows")
120123
beego.Router("/api/get-workflow", &controllers.ApiController{}, "GET:GetWorkflow")

0 commit comments

Comments
 (0)