MG-94 - Add backend support for reports (#107)

* initial implementation

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix missing variable

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix api and add report config to rule engine

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix repo command

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix download request

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix download api

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix add report config

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove unused parameters

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* add limit field to config

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* add test and address comments

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove unused code

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* add logger

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove logger

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* uncomment code

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* add status check

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* address comments

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* resolve conflicts

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* rebase code

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix startdate when zero

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove unused code

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* address comments

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* add time expression parser and logics

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix postgres methods

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix pdf and csv generation

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* add description for reports

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove aggregation field

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove unused code

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove logs

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix go mod file

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix endpoint and postgres methods

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* address comments

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* update report config update methods

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix service test

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove unnecessary check

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* address comments

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove endpoints

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove unused code

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix generate PDF and CSV

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove unused code

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* address comments

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* revert UI variable

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* add empty line

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix go mod file

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* update download api

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* revert UI variable

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix download endpoint

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* update generateREport method

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing tests

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* refactor generate api

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix failing linter

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix csv column

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix csv generator

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* remove logs

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* updated reports logic and api

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix time conversion

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

---------

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>
Co-authored-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
Steve Munene
2025-04-28 10:09:22 +03:00
committed by GitHub
parent 4dd0de64fb
commit 02da121280
35 changed files with 5450 additions and 221 deletions
+86
View File
@@ -0,0 +1,86 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package reltime
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
"github.com/absmach/magistrala/pkg/errors"
)
var (
re = regexp.MustCompile(`(?i)^now\(\)([\+\-])(.+)$`)
ErrInvalidDuration = errors.New("invalid duration format")
ErrInvalidExpression = errors.New("invalid time expression")
ErrUnsupportedUnit = errors.New("unsupported unit")
)
func Parse(expr string) (time.Time, error) {
now := time.Now()
expr = strings.ReplaceAll(expr, " ", "")
if strings.EqualFold(expr, "now()") {
return now, nil
}
matches := re.FindStringSubmatch(expr)
if len(matches) != 3 {
return time.Time{}, errors.Wrap(ErrInvalidExpression, fmt.Errorf("%s", expr))
}
sign := matches[1]
durStr := matches[2]
if strings.ContainsAny(durStr, "+-") {
return time.Time{}, errors.Wrap(ErrInvalidExpression, fmt.Errorf("%s", expr))
}
dur, err := parseComplexDuration(durStr)
if err != nil {
return time.Time{}, err
}
if sign == "-" {
return now.Add(-dur), nil
}
return now.Add(dur), nil
}
func parseComplexDuration(s string) (time.Duration, error) {
var total time.Duration
re := regexp.MustCompile(`(\d+)([smhdwMY])`)
matches := re.FindAllStringSubmatch(s, -1)
if matches == nil {
return 0, errors.Wrap(ErrInvalidDuration, fmt.Errorf("%s", s))
}
for _, match := range matches {
val, _ := strconv.Atoi(match[1])
unit := match[2]
var d time.Duration
switch unit {
case "s":
d = time.Duration(val) * time.Second
case "m":
d = time.Duration(val) * time.Minute
case "h":
d = time.Duration(val) * time.Hour
case "d":
d = time.Duration(val) * 24 * time.Hour
case "w":
d = time.Duration(val) * 7 * 24 * time.Hour
default:
return 0, errors.Wrap(ErrUnsupportedUnit, fmt.Errorf("%s", unit))
}
total += d
}
return total, nil
}
+76
View File
@@ -0,0 +1,76 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package reltime
import (
"fmt"
"testing"
"time"
"github.com/absmach/magistrala/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestParse(t *testing.T) {
now := time.Now()
tests := []struct {
desc string
expr string
expected time.Time
err error
}{
{
desc: "testing expression now()-5d",
expr: "now()-5d",
expected: now.Add(-5 * 24 * time.Hour),
err: nil,
},
{
desc: "testing expression now()+2h30m",
expr: "now()+2h30m",
expected: now.Add(2*time.Hour + 30*time.Minute),
err: nil,
},
{
desc: "testing expression now()-1w3d10h40m",
expr: "now()-1w3d10h40m",
expected: now.Add(-(7*24+3*24+10)*time.Hour - 40*time.Minute),
err: nil,
},
{
desc: "testing expression yesterday",
expr: "yesterday",
err: ErrInvalidExpression,
},
{
desc: "testing expression now()--5d",
expr: "now()--5d",
err: ErrInvalidExpression,
},
{
desc: "testing expression now()+",
expr: "now()+",
err: ErrInvalidExpression,
},
{
desc: "testing expression now()+5r",
expr: "now()+5r",
err: ErrInvalidDuration,
},
{
desc: "testing expression now()+5M",
expr: "now()+5M",
err: ErrUnsupportedUnit,
},
}
for _, tc := range tests {
got, err := Parse(tc.expr)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %v got %v and response time %v\n", tc.desc, tc.err, err, got))
if err == nil {
assert.WithinDuration(t, tc.expected, got, time.Duration(10*time.Second))
}
}
}