commit 55bad627c208c544780a6ecda6ebbdbf5d4ae4d9 Author: Joshua Herring Date: Sun Apr 5 11:39:32 2026 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4dd52a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +config.json +users.csv diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..00c29f5 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module maurer/user_data_generator + +go 1.26.1 + +require github.com/brianvoe/gofakeit/v6 v6.28.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7ae018c --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= +github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= diff --git a/main.go b/main.go new file mode 100644 index 0000000..9137b5c --- /dev/null +++ b/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "encoding/csv" + "encoding/json" + "fmt" + "log" + "math/rand" + "os" + + "github.com/brianvoe/gofakeit/v6" +) + +type User struct { + UID string `json:"uid"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Status string `json:"status"` + ExamID string `json:"exam_id"` +} + +type Config struct { + NumberOfUsers int `json:"number_of_users"` + PercentStatus map[string]int `json:"percent_status"` +} + +var statuses = []string{"JD", "LLM", "MCL", "PHD", "SJD", "MLS", "SPEC", "MNR", "CERT"} + +func main() { + data, err := os.ReadFile("config.json") + if err != nil { + log.Fatalf("failed to read config.json: %v", err) + } + + var config Config + if err := json.Unmarshal(data, &config); err != nil { + log.Fatalf("failed to parse config.json: %v", err) + } + + total := 0 + for _, v := range config.PercentStatus { + total += v + } + if total != 100 { + log.Fatalf("percent_status values must total 100, got %d", total) + } + + // Build a weighted list of statuses + weightedStatuses := make([]string, 0, 100) + for status, pct := range config.PercentStatus { + for i := 0; i < pct; i++ { + weightedStatuses = append(weightedStatuses, status) + } + } + + // Generate unique ExamIDs and UIDs + examIDs := uniqueNumericStrings(4, config.NumberOfUsers) + uids := uniqueNumericStrings(10, config.NumberOfUsers) + + users := make([]User, config.NumberOfUsers) + for i := range users { + users[i] = User{ + UID: uids[i], + FirstName: gofakeit.FirstName(), + LastName: gofakeit.LastName(), + Status: weightedStatuses[rand.Intn(len(weightedStatuses))], + ExamID: examIDs[i], + } + } + + f, err := os.Create("users.csv") + if err != nil { + log.Fatalf("failed to create users.csv: %v", err) + } + defer f.Close() + + w := csv.NewWriter(f) + if err := w.Write([]string{"UID", "FirstName", "LastName", "Status", "ExamID"}); err != nil { + log.Fatalf("failed to write csv header: %v", err) + } + for _, u := range users { + if err := w.Write([]string{u.UID, u.FirstName, u.LastName, u.Status, u.ExamID}); err != nil { + log.Fatalf("failed to write csv row: %v", err) + } + } + w.Flush() + if err := w.Error(); err != nil { + log.Fatalf("csv flush error: %v", err) + } + + fmt.Printf("Generated %d users to users.csv\n", config.NumberOfUsers) +} + +// uniqueNumericStrings generates n unique zero-padded numeric strings of the given digit length. +func uniqueNumericStrings(digits, n int) []string { + max := 1 + for i := 0; i < digits; i++ { + max *= 10 + } + if n > max { + log.Fatalf("cannot generate %d unique %d-digit strings (max %d)", n, digits, max) + } + + seen := make(map[int]struct{}, n) + results := make([]string, 0, n) + format := fmt.Sprintf("%%0%dd", digits) + for len(results) < n { + v := rand.Intn(max) + if _, exists := seen[v]; !exists { + seen[v] = struct{}{} + results = append(results, fmt.Sprintf(format, v)) + } + } + return results +}