common/repository/statistics/click_statistics.go
2024-06-14 16:51:47 +03:00

183 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package statistics
import (
"context"
"database/sql"
"fmt"
)
type DepsClick struct {
Conn *sql.DB
}
type StatisticClick struct {
conn *sql.DB
}
func NewClickStatistic(ctx context.Context, deps DepsClick) (*StatisticClick, error) {
s := &StatisticClick{
conn: deps.Conn,
}
err := s.checkMW(ctx)
if err != nil {
fmt.Println("error check material view existing")
return nil, err
}
return s, nil
}
// todo toanaliz for keydevice,keydevicetype,keybrowser,
// todo получается пока какая то фигня
func (s *StatisticClick) checkMW(ctx context.Context) error {
query := `
CREATE MATERIALIZED VIEW IF NOT EXISTS mv_last_answers_events
ENGINE = MergeTree()
ORDER BY (ctxsession, event_time) AS
SELECT
event_time, ctxsession, ctxquizid, ctxquestionid, ctxidint, message, keyos,
ctxuserip, ctxuserport, keydomain, keypath, ctxquiz, ctxreferrer
FROM (SELECT
event_time, ctxsession, ctxquizid, ctxquestionid, ctxidint, message, keyos,
ctxuserip, ctxuserport, keydomain, keypath, ctxquiz, ctxreferrer,
row_number() OVER (PARTITION BY ctxsession ORDER BY event_time DESC) as row_num
FROM statistics
WHERE message IN ('InfoQuizOpen', 'InfoAnswer', 'InfoResult') AND event_level = 'info') AS sorted
WHERE row_num = 1;
`
_, err := s.conn.ExecContext(ctx, query)
if err != nil {
return err
}
return nil
}
type Statistic struct {
Count int64
QuestionID int64
}
type PipeLineStatsResp [][]Statistic
type FunnelData struct {
QuestionID int64
Count int64
}
func (s *StatisticClick) funnelData(ctx context.Context, quizID int64, from uint64, to uint64) ([]FunnelData, error) {
query := `
SELECT ctxquestionid, uniqExact(ctxsession) AS count
FROM mv_last_answers_events
WHERE ctxquizid = ? AND event_time BETWEEN ? AND ?
GROUP BY ctxquestionid
`
rows, err := s.conn.QueryContext(ctx, query, quizID, from, to)
if err != nil {
return nil, err
}
defer rows.Close()
var data []FunnelData
for rows.Next() {
var questionID int64
var count int64
err := rows.Scan(&questionID, &count)
if err != nil {
return nil, err
}
data = append(data, FunnelData{
QuestionID: questionID,
Count: count,
})
}
if err := rows.Err(); err != nil {
return nil, err
}
return data, nil
}
// выборка вопросов с одной воронкой
func (s *StatisticClick) getOneFunnelQuestions(ctx context.Context, quizID int64, from uint64, to uint64) ([]int64, error) {
var result []int64
questionCounts, err := s.funnelData(ctx, quizID, from, to)
if err != nil {
return nil, err
}
for _, qc := range questionCounts {
if qc.Count == 1 {
result = append(result, qc.QuestionID)
}
}
return result, nil
}
func (s *StatisticClick) GetPipelinesStatistics(ctx context.Context, quizID int64, from uint64, to uint64) (PipeLineStatsResp, error) {
var pipelines PipeLineStatsResp
idS, err := s.getOneFunnelQuestions(ctx, quizID, from, to)
if err != nil {
return nil, err
}
query := `
SELECT
ctxsession,ctxquestionid,count(*) as session_count
FROM mv_last_answers_events WHERE ctxquizid = ? AND ctxquestionid = ? AND event_time BETWEEN ? AND ?
GROUP BY ctxsession, ctxquestionid ORDER BY ctxsession, ctxquestionid
`
for _, quiestionID := range idS {
rows, err := s.conn.QueryContext(ctx, query, quizID, quiestionID, from, to)
if err != nil {
return nil, err
}
defer rows.Close()
var currentPipeline []Statistic
var lastSession string
for rows.Next() {
var session string
var questionID int64
var count int64
err := rows.Scan(&session, &questionID, &count)
if err != nil {
return nil, err
}
// новая сессия - новая воронка
if session != lastSession {
if lastSession != "" {
pipelines = append(pipelines, currentPipeline)
}
currentPipeline = []Statistic{}
}
// текущая статистика в текущую воронку
currentPipeline = append(currentPipeline, Statistic{
Count: count,
QuestionID: questionID,
})
lastSession = session
}
// последня воронка если есть то добавляем
if len(currentPipeline) > 0 {
pipelines = append(pipelines, currentPipeline)
}
if err := rows.Err(); err != nil {
return nil, err
}
}
return pipelines, nil
}