Metrics and logs - MongoDB Submission (#43)
Adds MongoDB-backed submission for survey statistics/metrics and introduces MongoDB ObjectID-based participant/task identifiers to support upserts and linking metrics to a participant. It introduces breaking changes to the exsisting json stat and metric files. Delete the old ones Changes: Add internal/storage/MongoStorage and wire pm survey submit / pm start to submit local .pm/*.json files to MongoDB. Extend stats/metrics models to include MongoDB _id / participant_id fields and add participant ID propagation into task metrics. Update app initialization to generate ObjectIDs for new participants and expose GetParticipantID() via StatsService.
This commit is contained in:
@@ -3,6 +3,8 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
@@ -57,5 +59,6 @@ type StatsService interface {
|
||||
Load(ctx context.Context) error
|
||||
Save(ctx context.Context) error
|
||||
GetStatistics() (Statistics, error)
|
||||
GetParticipantID() primitive.ObjectID
|
||||
RecordTaskRun(ctx context.Context, run TaskRunMetrics) error
|
||||
}
|
||||
|
||||
@@ -1,24 +1,40 @@
|
||||
package models
|
||||
|
||||
import "os"
|
||||
|
||||
type Config struct {
|
||||
DbUri string
|
||||
AutoInit bool
|
||||
RootCmd string
|
||||
AppDir string
|
||||
WebAddress string
|
||||
BeadsDBPath string
|
||||
IssuePrefix string
|
||||
StatisticsStoragePath string
|
||||
ActionLogger func(string)
|
||||
StatisticsStoragePath string
|
||||
}
|
||||
|
||||
var BaseConfig = Config{
|
||||
DbUri: "",
|
||||
AutoInit: false,
|
||||
RootCmd: "pm",
|
||||
IssuePrefix: "pm",
|
||||
WebAddress: ":8080",
|
||||
BeadsDBPath: "./.pm/db.db",
|
||||
AppDir: "./.pm/",
|
||||
StatisticsStoragePath: "./.pm/stats.json",
|
||||
}
|
||||
|
||||
func (c Config) LoadFromEnv() Config {
|
||||
if dbURI, ok := os.LookupEnv("DB_URI"); ok {
|
||||
c.DbUri = dbURI
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c Config) WithDbUri(uri string) Config {
|
||||
c.DbUri = uri
|
||||
return c
|
||||
}
|
||||
|
||||
func (c Config) WithAutoInit(autoInit bool) Config {
|
||||
c.AutoInit = autoInit
|
||||
return c
|
||||
@@ -34,11 +50,6 @@ func (c Config) WithWebAddress(webAddress string) Config {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c Config) WithBeadsDBPath(beadsDBPath string) Config {
|
||||
c.BeadsDBPath = beadsDBPath
|
||||
return c
|
||||
}
|
||||
|
||||
func (c Config) WithIssuePrefix(issuePrefix string) Config {
|
||||
c.IssuePrefix = issuePrefix
|
||||
return c
|
||||
|
||||
@@ -2,91 +2,95 @@ package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type Statistics struct {
|
||||
ID int `json:"id"`
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Duration time.Duration `json:"duration"`
|
||||
ID primitive.ObjectID `bson:"_id" json:"id"`
|
||||
StartTime time.Time `bson:"start_time" json:"start_time"`
|
||||
EndTime time.Time `bson:"end_time" json:"end_time"`
|
||||
DurationMs int64 `bson:"duration_ms" json:"duration_ms"`
|
||||
|
||||
InterfaceType InterfaceType `json:"interface_type"`
|
||||
TaskRuns int `json:"task_runs"`
|
||||
TasksCompleted int `json:"tasks_completed"`
|
||||
TasksFailed int `json:"tasks_failed"`
|
||||
LastInterfaceType InterfaceType `bson:"last_interface_type" json:"last_interface_type"`
|
||||
TaskRuns int `bson:"task_runs" json:"task_runs"`
|
||||
TasksCompleted int `bson:"tasks_completed" json:"tasks_completed"`
|
||||
TasksFailed int `bson:"tasks_failed" json:"tasks_failed"`
|
||||
|
||||
TotalDurationMs int64 `json:"total_duration_ms"`
|
||||
AverageDurationMs int64 `json:"average_duration_ms"`
|
||||
TotalDurationMs int64 `bson:"total_duration_ms" json:"total_duration_ms"`
|
||||
AverageDurationMs int64 `bson:"average_duration_ms" json:"average_duration_ms"`
|
||||
|
||||
TotalUserActions int `json:"total_user_actions"`
|
||||
QuestionnairesCompleted int `json:"questionnaires_completed"`
|
||||
QuestionnairesAbandoned int `json:"questionnaires_abandoned"`
|
||||
TotalUserActions int `bson:"total_user_actions" json:"total_user_actions"`
|
||||
QuestionnairesCompleted int `bson:"questionnaires_completed" json:"questionnaires_completed"`
|
||||
QuestionnairesAbandoned int `bson:"questionnaires_abandoned" json:"questionnaires_abandoned"`
|
||||
|
||||
ValidationAttempts int `json:"validation_attempts"`
|
||||
ValidationSuccesses int `json:"validation_successes"`
|
||||
ValidationFailures int `json:"validation_failures"`
|
||||
ValidationChecksPassed int `json:"validation_checks_passed"`
|
||||
ValidationChecksFailed int `json:"validation_checks_failed"`
|
||||
ValidationAttempts int `bson:"validation_attempts" json:"validation_attempts"`
|
||||
ValidationSuccesses int `bson:"validation_successes" json:"validation_successes"`
|
||||
ValidationFailures int `bson:"validation_failures" json:"validation_failures"`
|
||||
ValidationChecksPassed int `bson:"validation_checks_passed" json:"validation_checks_passed"`
|
||||
ValidationChecksFailed int `bson:"validation_checks_failed" json:"validation_checks_failed"`
|
||||
|
||||
LastTaskName string `json:"last_task_name"`
|
||||
LastRunID int `json:"last_run_id"`
|
||||
LastTaskName string `bson:"last_task_name" json:"last_task_name"`
|
||||
LastRunID int `bson:"last_run_id" json:"last_run_id"`
|
||||
}
|
||||
|
||||
type TaskMetricsFile struct {
|
||||
TaskName string `json:"task_name"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Summary TaskStatsSummary `json:"summary"`
|
||||
Runs []TaskRunMetrics `json:"runs"`
|
||||
ID primitive.ObjectID `bson:"_id" json:"id"`
|
||||
ParticipantID primitive.ObjectID `bson:"participant_id" json:"participant_id"`
|
||||
TaskName string `bson:"task_name" json:"task_name"`
|
||||
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
|
||||
Summary TaskStatsSummary `bson:"summary" json:"summary"`
|
||||
Runs []TaskRunMetrics `bson:"runs" json:"runs"`
|
||||
}
|
||||
|
||||
type TaskStatsSummary struct {
|
||||
TotalRuns int `json:"total_runs"`
|
||||
CompletedRuns int `json:"completed_runs"`
|
||||
IncompleteRuns int `json:"incomplete_runs"`
|
||||
TotalDurationMs int64 `json:"total_duration_ms"`
|
||||
AverageDurationMs int64 `json:"average_duration_ms"`
|
||||
TotalUserActions int `json:"total_user_actions"`
|
||||
QuestionnairesCompleted int `json:"questionnaires_completed"`
|
||||
QuestionnairesAbandoned int `json:"questionnaires_abandoned"`
|
||||
ValidationAttempts int `json:"validation_attempts"`
|
||||
ValidationSuccesses int `json:"validation_successes"`
|
||||
ValidationFailures int `json:"validation_failures"`
|
||||
ValidationChecksPassed int `json:"validation_checks_passed"`
|
||||
ValidationChecksFailed int `json:"validation_checks_failed"`
|
||||
LastInterfaceType InterfaceType `json:"last_interface_type"`
|
||||
FirstRunStartedAt time.Time `json:"first_run_started_at"`
|
||||
LastRunStartedAt time.Time `json:"last_run_started_at"`
|
||||
LastRunEndedAt time.Time `json:"last_run_ended_at"`
|
||||
TotalRuns int `bson:"total_runs" json:"total_runs"`
|
||||
CompletedRuns int `bson:"completed_runs" json:"completed_runs"`
|
||||
IncompleteRuns int `bson:"incomplete_runs" json:"incomplete_runs"`
|
||||
TotalDurationMs int64 `bson:"total_duration_ms" json:"total_duration_ms"`
|
||||
AverageDurationMs int64 `bson:"average_duration_ms" json:"average_duration_ms"`
|
||||
TotalUserActions int `bson:"total_user_actions" json:"total_user_actions"`
|
||||
QuestionnairesCompleted int `bson:"questionnaires_completed" json:"questionnaires_completed"`
|
||||
QuestionnairesAbandoned int `bson:"questionnaires_abandoned" json:"questionnaires_abandoned"`
|
||||
ValidationAttempts int `bson:"validation_attempts" json:"validation_attempts"`
|
||||
ValidationSuccesses int `bson:"validation_successes" json:"validation_successes"`
|
||||
ValidationFailures int `bson:"validation_failures" json:"validation_failures"`
|
||||
ValidationChecksPassed int `bson:"validation_checks_passed" json:"validation_checks_passed"`
|
||||
ValidationChecksFailed int `bson:"validation_checks_failed" json:"validation_checks_failed"`
|
||||
LastInterfaceType InterfaceType `bson:"last_interface_type" json:"last_interface_type"`
|
||||
FirstRunStartedAt time.Time `bson:"first_run_started_at" json:"first_run_started_at"`
|
||||
LastRunStartedAt time.Time `bson:"last_run_started_at" json:"last_run_started_at"`
|
||||
LastRunEndedAt time.Time `bson:"last_run_ended_at" json:"last_run_ended_at"`
|
||||
}
|
||||
|
||||
type TaskRunMetrics struct {
|
||||
RunID int `json:"run_id"`
|
||||
TaskName string `json:"task_name"`
|
||||
InterfaceType InterfaceType `json:"interface_type"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
EndedAt time.Time `json:"ended_at"`
|
||||
DurationMs int64 `json:"duration_ms"`
|
||||
Completed bool `json:"completed"`
|
||||
ValidationAttempts int `json:"validation_attempts"`
|
||||
ValidationSuccesses int `json:"validation_successes"`
|
||||
ValidationFailures int `json:"validation_failures"`
|
||||
ValidationChecksPassed int `json:"validation_checks_passed"`
|
||||
ValidationChecksFailed int `json:"validation_checks_failed"`
|
||||
LastValidationMessage string `json:"last_validation_message,omitempty"`
|
||||
QuestionnaireCompleted bool `json:"questionnaire_completed"`
|
||||
QuestionnaireUserQuit bool `json:"questionnaire_user_quit"`
|
||||
QuestionnaireAnswers map[string]any `json:"questionnaire_answers,omitempty"`
|
||||
Logs []TaskLogEntry `json:"logs"`
|
||||
Error string `json:"error,omitempty"`
|
||||
RunID int `bson:"run_id" json:"run_id"`
|
||||
TaskName string `bson:"task_name" json:"task_name"`
|
||||
InterfaceType InterfaceType `bson:"interface_type" json:"interface_type"`
|
||||
StartedAt time.Time `bson:"started_at" json:"started_at"`
|
||||
EndedAt time.Time `bson:"ended_at" json:"ended_at"`
|
||||
DurationMs int64 `bson:"duration_ms" json:"duration_ms"`
|
||||
Completed bool `bson:"completed" json:"completed"`
|
||||
ValidationAttempts int `bson:"validation_attempts" json:"validation_attempts"`
|
||||
ValidationSuccesses int `bson:"validation_successes" json:"validation_successes"`
|
||||
ValidationFailures int `bson:"validation_failures" json:"validation_failures"`
|
||||
ValidationChecksPassed int `bson:"validation_checks_passed" json:"validation_checks_passed"`
|
||||
ValidationChecksFailed int `bson:"validation_checks_failed" json:"validation_checks_failed"`
|
||||
LastValidationMessage string `bson:"last_validation_message,omitempty" json:"last_validation_message,omitempty"`
|
||||
QuestionnaireCompleted bool `bson:"questionnaire_completed" json:"questionnaire_completed"`
|
||||
QuestionnaireUserQuit bool `bson:"questionnaire_user_quit" json:"questionnaire_user_quit"`
|
||||
QuestionnaireAnswers map[string]any `bson:"questionnaire_answers,omitempty" json:"questionnaire_answers,omitempty"`
|
||||
Logs []TaskLogEntry `bson:"logs" json:"logs"`
|
||||
Error string `bson:"error,omitempty" json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type TaskLogEntry struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Action string `json:"action,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Result string `json:"result,omitempty"`
|
||||
Attempt int `json:"attempt,omitempty"`
|
||||
Timestamp time.Time `bson:"timestamp" json:"timestamp"`
|
||||
Level string `bson:"level" json:"level"`
|
||||
Message string `bson:"message" json:"message"`
|
||||
Source string `bson:"source,omitempty" json:"source,omitempty"`
|
||||
Action string `bson:"action,omitempty" json:"action,omitempty"`
|
||||
Target string `bson:"target,omitempty" json:"target,omitempty"`
|
||||
Result string `bson:"result,omitempty" json:"result,omitempty"`
|
||||
Attempt int `bson:"attempt,omitempty" json:"attempt,omitempty"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user