common/repository/statistics/statistics.go

179 lines
6.6 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"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal/sqlcgen"
)
type Deps struct {
Queries *sqlcgen.Queries
Pool *sql.DB
}
type StatisticsRepository struct {
queries *sqlcgen.Queries
pool *sql.DB
}
func NewStatisticsRepo(deps Deps) *StatisticsRepository {
return &StatisticsRepository{
queries: deps.Queries,
pool: deps.Pool,
}
}
type DeviceStatReq struct {
QuizId int64
From uint64
To uint64
}
type DeviceStatResp struct {
//ключ DeviceType значение процент
Device map[string]float64 // процентное соотношение DeviceType по всем ответам на опроc c res==true
// тоже самое тут только по OS и BROWSER
OS map[string]float64
Browser map[string]float64
}
func (r *StatisticsRepository) GetDeviceStatistics(ctx context.Context, req DeviceStatReq) (DeviceStatResp, error) {
resp := DeviceStatResp{
Device: make(map[string]float64),
OS: make(map[string]float64),
Browser: make(map[string]float64),
}
allStatistics, err := r.queries.DeviceStatistics(ctx, sqlcgen.DeviceStatisticsParams{
QuizID: req.QuizId,
ToTimestamp: float64(req.From),
ToTimestamp_2: float64(req.To),
})
if err != nil {
return resp, err
}
for _, stat := range allStatistics {
resp.Device[stat.DeviceType] = stat.DevicePercentage
resp.OS[stat.Os] = stat.OsPercentage
resp.Browser[stat.Browser] = stat.BrowserPercentage
}
return resp, nil
}
type GeneralStatsResp struct {
Open map[int64]int64 // количество ответов с полем start == true за период от одного пункта разбиения и до другого
Result map[int64]int64 // количество ответов с полем result == true за период от одного пункта разбиения и до другого
AvTime map[int64]uint64 // среднее время между ответом с полем result == true и start == true. в рамках сессии
Conversion map[int64]float64 // Result/Open за период от одного пункта разбиения и до другого
}
func (r *StatisticsRepository) GetGeneralStatistics(ctx context.Context, req DeviceStatReq) (GeneralStatsResp, error) {
resp := GeneralStatsResp{
Open: make(map[int64]int64),
Result: make(map[int64]int64),
AvTime: make(map[int64]uint64),
Conversion: make(map[int64]float64),
}
// todo затестить запрос нужно, когда на один тру ответ приходится один тру старт апдейтнуть запрос
allStatistics, err := r.queries.GeneralStatistics(ctx, sqlcgen.GeneralStatisticsParams{
QuizID: req.QuizId,
ToTimestamp: float64(req.From),
ToTimestamp_2: float64(req.To),
})
if err != nil {
return resp, err
}
for _, stat := range allStatistics {
resp.Open[stat.TimeBucket.Unix()] = stat.OpenCount
resp.Result[stat.TimeBucket.Unix()] = stat.TrueResultCount
resp.AvTime[stat.TimeBucket.Unix()] = uint64(stat.AvgTime)
resp.Conversion[stat.TimeBucket.Unix()] = stat.Conversion
}
return resp, nil
}
type QuestionsStatsResp struct {
// PS это / не или а делить а то я спустя пару часов только догнал
//Funnel 3 отдельных метрики
// 0 - количество сессий с любым ответом кроме start == true / количество сессий с ответом start == true
// 1 - количество сессий с result == false, но тип вопроса, на который ответ == result / количество сессий с ответом start == true
// 2 - количество сессий с ответом result == true / количество сессий с ответом start == true
Funnel [3]float64
FunnelData [4]float64
// ключ - заголовок вопроса найденного по айдишнику вопроса в ответе result == true,
// значение - процент ответов с result == true и таким айдишником вопроса
Results map[string]float64
// ключ - заголовок вопроса, а значение - map, где ключ - вариант ответа на этот вопрос,
// т.е. группировка по полю Контент, а значение - процент таких ответов
Questions map[string]map[string]float64
}
func (r *StatisticsRepository) GetQuestionsStatistics(ctx context.Context, req DeviceStatReq) (QuestionsStatsResp, error) {
resp := QuestionsStatsResp{
Funnel: [3]float64{},
FunnelData: [4]float64{},
Results: make(map[string]float64),
Questions: make(map[string]map[string]float64),
}
queStatistics, err := r.queries.QuestionsStatistics(ctx, sqlcgen.QuestionsStatisticsParams{
QuizID: req.QuizId,
ToTimestamp: float64(req.From),
ToTimestamp_2: float64(req.To),
})
if err != nil {
return resp, err
}
for _, row := range queStatistics {
if row.CountStartTrue != 0 {
resp.Funnel[0] = float64(row.CountStartFalse) / float64(row.CountStartTrue)
resp.Funnel[1] = float64(row.CountFResultWithTQuestion) / float64(row.CountStartTrue)
resp.Funnel[2] = float64(row.CountTResult) / float64(row.CountStartTrue)
resp.FunnelData[0] = float64(row.CountStartTrue)
resp.FunnelData[1] = float64(row.CountStartFalse)
resp.FunnelData[2] = float64(row.CountFResultWithTQuestion)
resp.FunnelData[3] = float64(row.CountTResult)
}
resp.Results[row.ResultsTitle] = row.ResultsPercentage
if resp.Questions[row.QuestionsTitle] == nil {
resp.Questions[row.QuestionsTitle] = make(map[string]float64)
}
resp.Questions[row.QuestionsTitle][row.AnswerContent.String] = row.QuestionsPercentage
}
return resp, nil
}
type StatisticResp struct {
// от from до to
Registrations int64 // количество зарегестрированных аккаунтов
Quizes int64 // количество созданных не удаленных квизов
Results int64 // количество ответов с result = true
}
func (r *StatisticsRepository) AllServiceStatistics(ctx context.Context, from, to uint64) (StatisticResp, error) {
allSvcStats, err := r.queries.AllServiceStatistics(ctx, sqlcgen.AllServiceStatisticsParams{
ToTimestamp: float64(from),
ToTimestamp_2: float64(to),
})
if err != nil {
return StatisticResp{}, err
}
resp := StatisticResp{
Registrations: allSvcStats.Registrations,
Quizes: allSvcStats.Quizes,
Results: allSvcStats.Results,
}
return resp, nil
}