common/repository/statistics/click_statistics.go

207 lines
6.9 KiB
Go
Raw Normal View History

2024-06-13 12:39:18 +00:00
package statistics
2024-06-13 15:37:58 +00:00
import (
"context"
"database/sql"
"fmt"
)
2024-06-13 12:39:18 +00:00
type DepsClick struct {
Conn *sql.DB
}
type StatisticClick struct {
conn *sql.DB
}
2024-06-13 15:37:58 +00:00
func NewClickStatistic(ctx context.Context, deps DepsClick) (*StatisticClick, error) {
s := &StatisticClick{
2024-06-13 12:39:18 +00:00
conn: deps.Conn,
}
2024-06-13 15:37:58 +00:00
err := s.checkMW(ctx)
if err != nil {
fmt.Println("error check material view existing")
return nil, err
}
return s, nil
}
2024-06-13 16:01:44 +00:00
// todo toanaliz for keydevice,keydevicetype,keybrowser,
2024-06-13 15:37:58 +00:00
func (s *StatisticClick) checkMW(ctx context.Context) error {
2024-06-14 18:06:01 +00:00
// в мат вью получаем последний ссесионный координат, то есть тот момент на котором сессия в квизе завершилась
// не зависит какой это тип вопроса, страт, обычный, резулт, получаем всегда последний у сессии
// он определят конечное положение пользователя в опросе
2024-06-13 15:37:58 +00:00
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,
2024-06-14 11:22:18 +00:00
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;
2024-06-13 15:37:58 +00:00
`
_, err := s.conn.ExecContext(ctx, query)
if err != nil {
return err
}
return nil
}
2024-06-13 19:03:41 +00:00
type Statistic struct {
2024-06-13 15:37:58 +00:00
Count int64
QuestionID int64
}
2024-06-13 19:03:41 +00:00
type PipeLineStatsResp [][]Statistic
2024-06-14 13:51:47 +00:00
type FunnelData struct {
2024-06-14 18:06:01 +00:00
Session string
QuestionIDs []int64
2024-06-14 13:51:47 +00:00
}
func (s *StatisticClick) funnelData(ctx context.Context, quizID int64, from uint64, to uint64) ([]FunnelData, error) {
2024-06-14 18:06:01 +00:00
// получили все уникальные сессии из мат вью
sessionQuery := `
SELECT DISTINCT ctxsession
FROM mv_last_answers_events
WHERE ctxquizid = ? AND event_time BETWEEN ? AND ?
`
sessionRows, err := s.conn.QueryContext(ctx, sessionQuery, quizID, from, to)
2024-06-13 15:37:58 +00:00
if err != nil {
return nil, err
}
2024-06-14 18:06:01 +00:00
defer sessionRows.Close()
2024-06-14 13:51:47 +00:00
2024-06-14 18:06:01 +00:00
var funnelData []FunnelData
for sessionRows.Next() {
var ctxsession string
err := sessionRows.Scan(&ctxsession)
2024-06-13 15:37:58 +00:00
if err != nil {
return nil, err
}
2024-06-14 18:06:01 +00:00
// получаем вопросы уникальные, последние в рамках сессии
questionQuery := `
SELECT DISTINCT ctxquestionid
FROM statistics WHERE ctxsession = ? ORDER BY event_time DESC;
`
questionRows, err := s.conn.QueryContext(ctx, questionQuery, ctxsession)
if err != nil {
return nil, err
}
defer questionRows.Close()
var questions []int64
for questionRows.Next() {
var ctxquestionid int64
err := questionRows.Scan(&ctxquestionid)
if err != nil {
return nil, err
}
questions = append(questions, ctxquestionid)
}
fmt.Println("questions", questions)
if err := questionRows.Err(); err != nil {
return nil, err
}
2024-06-13 15:37:58 +00:00
2024-06-14 18:06:01 +00:00
funnelData = append(funnelData, FunnelData{
Session: ctxsession,
QuestionIDs: questions,
2024-06-13 19:03:41 +00:00
})
2024-06-14 13:51:47 +00:00
}
2024-06-13 15:37:58 +00:00
2024-06-14 18:06:01 +00:00
if err := sessionRows.Err(); err != nil {
2024-06-14 13:51:47 +00:00
return nil, err
2024-06-13 15:37:58 +00:00
}
2024-06-14 18:06:01 +00:00
return funnelData, nil
2024-06-14 13:51:47 +00:00
}
2024-06-14 18:06:01 +00:00
// todo еще раз понять подумать осознать как подписывать воронку сейчас если массив соддержит 1 элемент это старт ответ
2024-06-14 13:51:47 +00:00
func (s *StatisticClick) getOneFunnelQuestions(ctx context.Context, quizID int64, from uint64, to uint64) ([]int64, error) {
var result []int64
2024-06-14 18:06:01 +00:00
funnelData, err := s.funnelData(ctx, quizID, from, to)
2024-06-14 13:51:47 +00:00
if err != nil {
return nil, err
2024-06-13 15:37:58 +00:00
}
2024-06-14 18:06:01 +00:00
for _, fd := range funnelData {
if len(fd.QuestionIDs) == 1 {
result = append(result, fd.QuestionIDs...)
2024-06-14 13:51:47 +00:00
}
}
return result, nil
}
func (s *StatisticClick) GetPipelinesStatistics(ctx context.Context, quizID int64, from uint64, to uint64) (PipeLineStatsResp, error) {
2024-06-14 18:06:01 +00:00
// тут нужно подписать все вопросы, которые принадлежат окончанию сессии, то есть массив ответов до окончания сессии
// это делается для каждой из сессии которая имеется в мат вью, это отношение к результату которого достиг респондент
// для того чтобы получить индентификатор воронки, то есть тот который последний этап прохождения был, то на чем все закончилось
// нужно из всех подписанных которые подписываются массивом и нужно выбрать из всех подписанных ответов на вопросы, выбрать те
// которые имеет всего один элемент в списке, то есть достижение никакого другого вопроса или результата не проходило через
// этот ответа кроме попытки достигнуть этотго оттвета или результата, это и являются конечные точки прохождения вопросов и от них считаются воронки и являются индентификаторами воронки
// именно те которые с массивом с 1 элементом
// нужно вернуть в каждом списке пару, id вопроса и количество ответов на этот вопрос, count - уникальные сессии которые прошли через этот вопрос
2024-06-14 13:51:47 +00:00
var pipelines PipeLineStatsResp
idS, err := s.getOneFunnelQuestions(ctx, quizID, from, to)
if err != nil {
2024-06-13 15:37:58 +00:00
return nil, err
}
2024-06-14 18:06:01 +00:00
for _, questionID := range idS {
query := `
SELECT ctxquestionid, COUNT(DISTINCT ctxsession) AS session_count
FROM mv_last_answers_events
WHERE ctxquizid = ? AND ctxquestionid = ? AND event_time BETWEEN ? AND ?
GROUP BY ctxquestionid
`
2024-06-14 13:51:47 +00:00
2024-06-14 18:06:01 +00:00
rows, err := s.conn.QueryContext(ctx, query, quizID, questionID, from, to)
2024-06-14 13:51:47 +00:00
if err != nil {
return nil, err
}
defer rows.Close()
var currentPipeline []Statistic
for rows.Next() {
2024-06-14 18:06:01 +00:00
var id int64
2024-06-14 13:51:47 +00:00
var count int64
2024-06-14 18:06:01 +00:00
err := rows.Scan(&id, &count)
2024-06-14 13:51:47 +00:00
if err != nil {
return nil, err
}
currentPipeline = append(currentPipeline, Statistic{
2024-06-14 18:06:01 +00:00
QuestionID: id,
2024-06-14 13:51:47 +00:00
Count: count,
})
2024-06-14 18:06:01 +00:00
}
2024-06-14 13:51:47 +00:00
2024-06-14 18:06:01 +00:00
if err := rows.Err(); err != nil {
return nil, err
2024-06-14 13:51:47 +00:00
}
if len(currentPipeline) > 0 {
pipelines = append(pipelines, currentPipeline)
}
}
2024-06-13 15:37:58 +00:00
return pipelines, nil
2024-06-13 12:39:18 +00:00
}