improve on the subimsion process with automatic submissions

make sure we give each user uniqe id
This commit is contained in:
Robin Olsen
2026-03-09 13:23:46 +01:00
parent d7c52383be
commit 5c4ecc841c
12 changed files with 319 additions and 184 deletions

View File

@@ -169,6 +169,8 @@ func commandNeedsApp(cmd *cobra.Command) bool {
func ensureAppInitialized(ctx context.Context) error { func ensureAppInitialized(ctx context.Context) error {
if App != nil { if App != nil {
survey.SetApp(App)
issues.SetApp(App)
return nil return nil
} }

View File

@@ -5,10 +5,13 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/rand" "math/rand"
"time"
"github.com/LazyBachelor/LazyPM/cmd/pm/tasks" "github.com/LazyBachelor/LazyPM/cmd/pm/tasks"
"github.com/LazyBachelor/LazyPM/internal/commands/survey" "github.com/LazyBachelor/LazyPM/internal/commands/survey"
"github.com/LazyBachelor/LazyPM/internal/storage"
"github.com/LazyBachelor/LazyPM/pkg/task" "github.com/LazyBachelor/LazyPM/pkg/task"
"github.com/charmbracelet/huh"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -28,6 +31,54 @@ func runStartCmd(cmd *cobra.Command, args []string) error {
return fmt.Errorf("application services are not available") return fmt.Errorf("application services are not available")
} }
if len(args) == 0 {
var noSubmit bool
cmd.Println("No MongoDB password provided, survey responses will not be submitted.")
cmd.Println("you can manually submit later with `pm survey submit <mongo-password>`.")
huh.NewConfirm().Title("Are you sure you want to continue without submiting data?").
Value(&noSubmit).RunAccessible(cmd.OutOrStdout(), cmd.InOrStdin())
if !noSubmit {
cmd.Println("Exiting. Please run the command again with a MongoDB password to submit your data.")
return nil
}
} else {
mongoPass := args[0]
mongoStorage, err := storage.NewMongoStorage(app.Config.MongoURI, "participant", mongoPass)
if err != nil {
cmd.Println("Failed to connect to MongoDB, survey responses will not be submitted.")
cmd.Println("You can manually submit later with `pm survey submit <password>`.")
return nil
} else {
cmd.Println("Connected to MongoDB successfully.")
defer mongoStorage.Close()
ctx := cmd.Context()
go func() {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if err := mongoStorage.SubmitSurveyResponsesCmd(ctx); err != nil {
cmd.Printf("Failed to submit survey responses: %v\n", err)
}
}
}
}()
if err := mongoStorage.SubmitSurveyResponsesCmd(ctx); err != nil {
cmd.Printf("Failed to submit survey responses: %v\n", err)
}
}
}
interfaces := initInterfaces() interfaces := initInterfaces()
surveyTasks := initTasks(app) surveyTasks := initTasks(app)

View File

@@ -7,6 +7,7 @@ import (
"github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/models"
"github.com/LazyBachelor/LazyPM/internal/storage" "github.com/LazyBachelor/LazyPM/internal/storage"
"github.com/steveyegge/beads" "github.com/steveyegge/beads"
"go.mongodb.org/mongo-driver/bson/primitive"
) )
type App = models.App type App = models.App
@@ -42,7 +43,7 @@ func New(ctx context.Context, config Config, opts ...Option) (*App, func(), erro
if b.statsService == nil { if b.statsService == nil {
statStore := storage.NewJsonStorage(config.StatisticsStoragePath, &models.Statistics{ statStore := storage.NewJsonStorage(config.StatisticsStoragePath, &models.Statistics{
ID: 0, ID: primitive.NewObjectID(),
StartTime: time.Now(), StartTime: time.Now(),
}) })

View File

@@ -9,6 +9,7 @@ import (
"github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/models"
"github.com/LazyBachelor/LazyPM/internal/storage" "github.com/LazyBachelor/LazyPM/internal/storage"
"go.mongodb.org/mongo-driver/bson/primitive"
) )
type StatisticsService struct { type StatisticsService struct {
@@ -42,6 +43,16 @@ func (s *StatisticsService) GetStatistics() (models.Statistics, error) {
return *s.storage.Data, nil return *s.storage.Data, nil
} }
func (s *StatisticsService) GetParticipantID() primitive.ObjectID {
s.mu.Lock()
defer s.mu.Unlock()
if s.storage.Data == nil {
return primitive.NilObjectID
}
return s.storage.Data.ID
}
func (s *StatisticsService) RecordTaskRun(ctx context.Context, run models.TaskRunMetrics) error { func (s *StatisticsService) RecordTaskRun(ctx context.Context, run models.TaskRunMetrics) error {
_ = ctx _ = ctx

View File

@@ -26,11 +26,6 @@ Your responses will be kept confidential and used solely for research purposes.`
var RootCmd = &cobra.Command{ var RootCmd = &cobra.Command{
Use: "survey", Use: "survey",
Long: long, Long: long,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if app != nil {
cmd.SetContext(context.WithValue(cmd.Context(), appKey, app))
}
},
} }
func SetApp(application *App) { func SetApp(application *App) {

View File

@@ -1,123 +1,40 @@
package survey package survey
import ( import (
"encoding/json"
"fmt" "fmt"
"os"
"strings"
"github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/storage"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
) )
var mongoURI = os.Getenv("MONGODB_URI")
var SubmitCmd = &cobra.Command{ var SubmitCmd = &cobra.Command{
Use: "submit", Use: "submit <mongo-password>",
Short: "Submit your survey responses", Short: "Submit your survey responses",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
client, error := mongo.Connect(cmd.Context(), options.Client().ApplyURI(mongoURI)) app := AppFromContext(cmd.Context())
if error != nil {
return fmt.Errorf("Failed to connect to MongoDB: %v", error) if app == nil {
return fmt.Errorf("application context not initialized")
} }
go func() { if len(args) == 0 {
if err := client.Disconnect(cmd.Context()); err != nil { cmd.Println("No MongoDB password provided, survey responses will not be submitted.")
fmt.Printf("Failed to disconnect MongoDB client: %v", err) return nil
} }
}()
userStatscollection := client.Database("Responses").Collection("stats") mongoPassword := args[0]
taskMetricsCollection := client.Database("Responses").Collection("task_metrics") mongoClient, err := storage.NewMongoStorage(app.Config.MongoURI, "participant", mongoPassword)
pmDir := "./.pm/"
entries, err := os.ReadDir(pmDir)
if err != nil { if err != nil {
return fmt.Errorf("Failed to read .pm directory: %v", err) return fmt.Errorf("failed to connect to MongoDB: %w", err)
}
defer mongoClient.Close()
if err := mongoClient.SubmitSurveyResponsesCmd(cmd.Context()); err != nil {
return fmt.Errorf("failed to submit survey responses: %w", err)
} }
if len(entries) == 0 { cmd.Println("Successfully submitted survey responses and metrics to the database")
return fmt.Errorf("No files found in .pm directory")
}
statFile := pmDir + "stats.json"
if _, err := os.Stat(statFile); os.IsNotExist(err) {
return fmt.Errorf("stats.json not found in .pm directory")
}
stats, err := getStats(statFile)
if err != nil {
return fmt.Errorf("Failed to read stats.json: %v", err)
}
_, err = userStatscollection.InsertOne(cmd.Context(), stats)
if err != nil {
return fmt.Errorf("Failed to insert stats into database: %v", err)
}
metricFiles := []string{}
for _, entry := range entries {
if entry.IsDir() {
continue
}
if entry.Name() == "stats.json" {
continue
}
if strings.HasSuffix(entry.Name(), "-stats.json") {
metricFiles = append(metricFiles, pmDir+entry.Name())
continue
}
}
for _, file := range metricFiles {
metrics, err := getTaskMetrics(file)
if err != nil {
fmt.Printf("failed to read metrics from %s: %v", file, err)
continue
}
_, err = taskMetricsCollection.InsertOne(cmd.Context(), metrics)
if err != nil {
fmt.Printf("failed to insert metrics from %s: %v", file, err)
continue
}
}
cmd.Printf("Successfully submitted survey responses and metrics to the database")
return nil return nil
}, },
} }
func getStats(file string) (*models.Statistics, error) {
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var stats models.Statistics
if err := json.Unmarshal(data, &stats); err != nil {
return nil, err
}
return &stats, nil
}
func getTaskMetrics(file string) (*models.TaskMetricsFile, error) {
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var metrics models.TaskMetricsFile
if err := json.Unmarshal(data, &metrics); err != nil {
return nil, err
}
return &metrics, nil
}

View File

@@ -3,6 +3,8 @@ package models
import ( import (
"context" "context"
"log/slog" "log/slog"
"go.mongodb.org/mongo-driver/bson/primitive"
) )
type App struct { type App struct {
@@ -57,5 +59,6 @@ type StatsService interface {
Load(ctx context.Context) error Load(ctx context.Context) error
Save(ctx context.Context) error Save(ctx context.Context) error
GetStatistics() (Statistics, error) GetStatistics() (Statistics, error)
GetParticipantID() primitive.ObjectID
RecordTaskRun(ctx context.Context, run TaskRunMetrics) error RecordTaskRun(ctx context.Context, run TaskRunMetrics) error
} }

View File

@@ -1,6 +1,7 @@
package models package models
type Config struct { type Config struct {
MongoURI string
AutoInit bool AutoInit bool
RootCmd string RootCmd string
WebAddress string WebAddress string
@@ -11,6 +12,7 @@ type Config struct {
} }
var BaseConfig = Config{ var BaseConfig = Config{
MongoURI: "mongodb+srv://lazy.wf9kdi8.mongodb.net/",
AutoInit: false, AutoInit: false,
RootCmd: "pm", RootCmd: "pm",
IssuePrefix: "pm", IssuePrefix: "pm",
@@ -19,6 +21,11 @@ var BaseConfig = Config{
StatisticsStoragePath: "./.pm/stats.json", StatisticsStoragePath: "./.pm/stats.json",
} }
func (c Config) WithMongoURI(uri string) Config {
c.MongoURI = uri
return c
}
func (c Config) WithAutoInit(autoInit bool) Config { func (c Config) WithAutoInit(autoInit bool) Config {
c.AutoInit = autoInit c.AutoInit = autoInit
return c return c

View File

@@ -2,91 +2,95 @@ package models
import ( import (
"time" "time"
"go.mongodb.org/mongo-driver/bson/primitive"
) )
type Statistics struct { type Statistics struct {
ID int `json:"id"` ID primitive.ObjectID `bson:"_id" json:"id"`
StartTime time.Time `json:"start_time"` StartTime time.Time `bson:"start_time" json:"start_time"`
EndTime time.Time `json:"end_time"` EndTime time.Time `bson:"end_time" json:"end_time"`
Duration time.Duration `json:"duration"` Duration time.Duration `bson:"duration" json:"duration"`
InterfaceType InterfaceType `json:"interface_type"` InterfaceType InterfaceType `bson:"interface_type" json:"interface_type"`
TaskRuns int `json:"task_runs"` TaskRuns int `bson:"task_runs" json:"task_runs"`
TasksCompleted int `json:"tasks_completed"` TasksCompleted int `bson:"tasks_completed" json:"tasks_completed"`
TasksFailed int `json:"tasks_failed"` TasksFailed int `bson:"tasks_failed" json:"tasks_failed"`
TotalDurationMs int64 `json:"total_duration_ms"` TotalDurationMs int64 `bson:"total_duration_ms" json:"total_duration_ms"`
AverageDurationMs int64 `json:"average_duration_ms"` AverageDurationMs int64 `bson:"average_duration_ms" json:"average_duration_ms"`
TotalUserActions int `json:"total_user_actions"` TotalUserActions int `bson:"total_user_actions" json:"total_user_actions"`
QuestionnairesCompleted int `json:"questionnaires_completed"` QuestionnairesCompleted int `bson:"questionnaires_completed" json:"questionnaires_completed"`
QuestionnairesAbandoned int `json:"questionnaires_abandoned"` QuestionnairesAbandoned int `bson:"questionnaires_abandoned" json:"questionnaires_abandoned"`
ValidationAttempts int `json:"validation_attempts"` ValidationAttempts int `bson:"validation_attempts" json:"validation_attempts"`
ValidationSuccesses int `json:"validation_successes"` ValidationSuccesses int `bson:"validation_successes" json:"validation_successes"`
ValidationFailures int `json:"validation_failures"` ValidationFailures int `bson:"validation_failures" json:"validation_failures"`
ValidationChecksPassed int `json:"validation_checks_passed"` ValidationChecksPassed int `bson:"validation_checks_passed" json:"validation_checks_passed"`
ValidationChecksFailed int `json:"validation_checks_failed"` ValidationChecksFailed int `bson:"validation_checks_failed" json:"validation_checks_failed"`
LastTaskName string `json:"last_task_name"` LastTaskName string `bson:"last_task_name" json:"last_task_name"`
LastRunID int `json:"last_run_id"` LastRunID int `bson:"last_run_id" json:"last_run_id"`
} }
type TaskMetricsFile struct { type TaskMetricsFile struct {
TaskName string `json:"task_name"` ID primitive.ObjectID `bson:"_id" json:"id"`
UpdatedAt time.Time `json:"updated_at"` ParticipantID primitive.ObjectID `bson:"participant_id" json:"participant_id"`
Summary TaskStatsSummary `json:"summary"` TaskName string `bson:"task_name" json:"task_name"`
Runs []TaskRunMetrics `json:"runs"` UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
Summary TaskStatsSummary `bson:"summary" json:"summary"`
Runs []TaskRunMetrics `bson:"runs" json:"runs"`
} }
type TaskStatsSummary struct { type TaskStatsSummary struct {
TotalRuns int `json:"total_runs"` TotalRuns int `bson:"total_runs" json:"total_runs"`
CompletedRuns int `json:"completed_runs"` CompletedRuns int `bson:"completed_runs" json:"completed_runs"`
IncompleteRuns int `json:"incomplete_runs"` IncompleteRuns int `bson:"incomplete_runs" json:"incomplete_runs"`
TotalDurationMs int64 `json:"total_duration_ms"` TotalDurationMs int64 `bson:"total_duration_ms" json:"total_duration_ms"`
AverageDurationMs int64 `json:"average_duration_ms"` AverageDurationMs int64 `bson:"average_duration_ms" json:"average_duration_ms"`
TotalUserActions int `json:"total_user_actions"` TotalUserActions int `bson:"total_user_actions" json:"total_user_actions"`
QuestionnairesCompleted int `json:"questionnaires_completed"` QuestionnairesCompleted int `bson:"questionnaires_completed" json:"questionnaires_completed"`
QuestionnairesAbandoned int `json:"questionnaires_abandoned"` QuestionnairesAbandoned int `bson:"questionnaires_abandoned" json:"questionnaires_abandoned"`
ValidationAttempts int `json:"validation_attempts"` ValidationAttempts int `bson:"validation_attempts" json:"validation_attempts"`
ValidationSuccesses int `json:"validation_successes"` ValidationSuccesses int `bson:"validation_successes" json:"validation_successes"`
ValidationFailures int `json:"validation_failures"` ValidationFailures int `bson:"validation_failures" json:"validation_failures"`
ValidationChecksPassed int `json:"validation_checks_passed"` ValidationChecksPassed int `bson:"validation_checks_passed" json:"validation_checks_passed"`
ValidationChecksFailed int `json:"validation_checks_failed"` ValidationChecksFailed int `bson:"validation_checks_failed" json:"validation_checks_failed"`
LastInterfaceType InterfaceType `json:"last_interface_type"` LastInterfaceType InterfaceType `bson:"last_interface_type" json:"last_interface_type"`
FirstRunStartedAt time.Time `json:"first_run_started_at"` FirstRunStartedAt time.Time `bson:"first_run_started_at" json:"first_run_started_at"`
LastRunStartedAt time.Time `json:"last_run_started_at"` LastRunStartedAt time.Time `bson:"last_run_started_at" json:"last_run_started_at"`
LastRunEndedAt time.Time `json:"last_run_ended_at"` LastRunEndedAt time.Time `bson:"last_run_ended_at" json:"last_run_ended_at"`
} }
type TaskRunMetrics struct { type TaskRunMetrics struct {
RunID int `json:"run_id"` RunID int `bson:"run_id" json:"run_id"`
TaskName string `json:"task_name"` TaskName string `bson:"task_name" json:"task_name"`
InterfaceType InterfaceType `json:"interface_type"` InterfaceType InterfaceType `bson:"interface_type" json:"interface_type"`
StartedAt time.Time `json:"started_at"` StartedAt time.Time `bson:"started_at" json:"started_at"`
EndedAt time.Time `json:"ended_at"` EndedAt time.Time `bson:"ended_at" json:"ended_at"`
DurationMs int64 `json:"duration_ms"` DurationMs int64 `bson:"duration_ms" json:"duration_ms"`
Completed bool `json:"completed"` Completed bool `bson:"completed" json:"completed"`
ValidationAttempts int `json:"validation_attempts"` ValidationAttempts int `bson:"validation_attempts" json:"validation_attempts"`
ValidationSuccesses int `json:"validation_successes"` ValidationSuccesses int `bson:"validation_successes" json:"validation_successes"`
ValidationFailures int `json:"validation_failures"` ValidationFailures int `bson:"validation_failures" json:"validation_failures"`
ValidationChecksPassed int `json:"validation_checks_passed"` ValidationChecksPassed int `bson:"validation_checks_passed" json:"validation_checks_passed"`
ValidationChecksFailed int `json:"validation_checks_failed"` ValidationChecksFailed int `bson:"validation_checks_failed" json:"validation_checks_failed"`
LastValidationMessage string `json:"last_validation_message,omitempty"` LastValidationMessage string `bson:"last_validation_message,omitempty" json:"last_validation_message,omitempty"`
QuestionnaireCompleted bool `json:"questionnaire_completed"` QuestionnaireCompleted bool `bson:"questionnaire_completed" json:"questionnaire_completed"`
QuestionnaireUserQuit bool `json:"questionnaire_user_quit"` QuestionnaireUserQuit bool `bson:"questionnaire_user_quit" json:"questionnaire_user_quit"`
QuestionnaireAnswers map[string]any `json:"questionnaire_answers,omitempty"` QuestionnaireAnswers map[string]any `bson:"questionnaire_answers,omitempty" json:"questionnaire_answers,omitempty"`
Logs []TaskLogEntry `json:"logs"` Logs []TaskLogEntry `bson:"logs" json:"logs"`
Error string `json:"error,omitempty"` Error string `bson:"error,omitempty" json:"error,omitempty"`
} }
type TaskLogEntry struct { type TaskLogEntry struct {
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `bson:"timestamp" json:"timestamp"`
Level string `json:"level"` Level string `bson:"level" json:"level"`
Message string `json:"message"` Message string `bson:"message" json:"message"`
Source string `json:"source,omitempty"` Source string `bson:"source,omitempty" json:"source,omitempty"`
Action string `json:"action,omitempty"` Action string `bson:"action,omitempty" json:"action,omitempty"`
Target string `json:"target,omitempty"` Target string `bson:"target,omitempty" json:"target,omitempty"`
Result string `json:"result,omitempty"` Result string `bson:"result,omitempty" json:"result,omitempty"`
Attempt int `json:"attempt,omitempty"` Attempt int `bson:"attempt,omitempty" json:"attempt,omitempty"`
} }

136
internal/storage/mongo.go Normal file
View File

@@ -0,0 +1,136 @@
package storage
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/LazyBachelor/LazyPM/internal/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type MongoStorage struct {
client *mongo.Client
}
func NewMongoStorage(uri, username, password string) (*MongoStorage, error) {
credentials := options.Credential{
Username: username,
Password: password,
}
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(uri).SetAuth(credentials))
if err != nil {
return nil, fmt.Errorf("failed to connect to MongoDB: %v", err)
}
if err := client.Ping(context.Background(), nil); err != nil {
return nil, fmt.Errorf("cannot reach MongoDB: %v", err)
}
return &MongoStorage{client: client}, nil
}
func (s *MongoStorage) Close() error {
return s.client.Disconnect(context.Background())
}
func (s *MongoStorage) SubmitSurveyResponsesCmd(ctx context.Context) error {
pmDir := "./.pm/"
userStatscollection := s.client.Database("Responses").Collection("stats")
taskMetricsCollection := s.client.Database("Responses").Collection("metrics")
entries, err := os.ReadDir(pmDir)
if err != nil {
return fmt.Errorf("Failed to read .pm directory: %v", err)
}
if len(entries) == 0 {
return fmt.Errorf("No files found in .pm directory")
}
statFile := pmDir + "stats.json"
if _, err := os.Stat(statFile); os.IsNotExist(err) {
return fmt.Errorf("stats.json not found in .pm directory")
}
stats, err := getStats(statFile)
if err != nil {
return fmt.Errorf("Failed to read stats.json: %v", err)
}
_, err = userStatscollection.UpdateOne(ctx,
bson.M{"_id": stats.ID},
bson.M{"$set": stats},
options.Update().SetUpsert(true))
if err != nil {
return fmt.Errorf("Failed to insert stats into database: %v", err)
}
metricFiles := []string{}
for _, entry := range entries {
if entry.IsDir() {
continue
}
if entry.Name() == "stats.json" {
continue
}
if strings.HasSuffix(entry.Name(), "-stats.json") {
metricFiles = append(metricFiles, pmDir+entry.Name())
continue
}
}
for _, file := range metricFiles {
metrics, err := getTaskMetrics(file)
if err != nil {
fmt.Printf("failed to read metrics from %s: %v", file, err)
continue
}
_, err = taskMetricsCollection.UpdateOne(ctx, bson.M{"_id": metrics.ID}, bson.M{"$set": metrics},
options.Update().SetUpsert(true))
if err != nil {
fmt.Printf("failed to insert metrics from %s: %v", file, err)
continue
}
}
return nil
}
func getStats(file string) (*models.Statistics, error) {
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var stats models.Statistics
if err := json.Unmarshal(data, &stats); err != nil {
return nil, err
}
return &stats, nil
}
func getTaskMetrics(file string) (*models.TaskMetricsFile, error) {
data, err := os.ReadFile(file)
if err != nil {
return nil, err
}
var metrics models.TaskMetricsFile
if err := json.Unmarshal(data, &metrics); err != nil {
return nil, err
}
return &metrics, nil
}

View File

@@ -4,6 +4,7 @@ import (
"log/slog" "log/slog"
"github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/models"
"go.mongodb.org/mongo-driver/bson/primitive"
) )
type RunLifecycle struct { type RunLifecycle struct {
@@ -23,9 +24,11 @@ func NewRunLifecycle(app *App, config Config, details models.TaskDetails, iType
collector.recordUserAction(action) collector.recordUserAction(action)
}) })
var participantID primitive.ObjectID
var store MetricsStore var store MetricsStore
if config.StatisticsStoragePath != "" { if config.StatisticsStoragePath != "" {
store = NewFileMetricsStore(config.StatisticsStoragePath, logger) participantID = app.Stats.GetParticipantID()
store = NewFileMetricsStore(config.StatisticsStoragePath, participantID, logger)
} }
return &RunLifecycle{ return &RunLifecycle{

View File

@@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/models"
"go.mongodb.org/mongo-driver/bson/primitive"
) )
type MetricsStore interface { type MetricsStore interface {
@@ -17,14 +18,16 @@ type MetricsStore interface {
} }
type FileMetricsStore struct { type FileMetricsStore struct {
path string path string
logger *slog.Logger participantID primitive.ObjectID
logger *slog.Logger
} }
func NewFileMetricsStore(path string, logger *slog.Logger) *FileMetricsStore { func NewFileMetricsStore(path string, participantID primitive.ObjectID, logger *slog.Logger) *FileMetricsStore {
return &FileMetricsStore{ return &FileMetricsStore{
path: path, path: path,
logger: logger, participantID: participantID,
logger: logger,
} }
} }
@@ -41,8 +44,10 @@ func (s *FileMetricsStore) Append(ctx context.Context, taskName string, run mode
} }
metrics := models.TaskMetricsFile{ metrics := models.TaskMetricsFile{
TaskName: taskName, ID: primitive.NewObjectID(),
Runs: []models.TaskRunMetrics{}, ParticipantID: s.participantID,
TaskName: taskName,
Runs: []models.TaskRunMetrics{},
} }
if err := readMetrics(&metrics, s.path); err != nil { if err := readMetrics(&metrics, s.path); err != nil {