package main import ( "encoding/csv" "encoding/json" "fmt" "log" "math/rand" "os" "github.com/brianvoe/gofakeit/v6" ) var course_type = []string{ "A", "B", "C", "D", "E", } 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 Section struct { SectionID string `json:"section_id"` CourseNumber string `json:"course_number"` Instructor string `json:"course_instructor"` CourseTitle string `json:"course_title"` CourseType string `json:"course_type"` } type Config struct { NumberOfUsers int `json:"number_of_users"` NumberOfSections int `json:"number_of_sections"` NumberOfInstructors int `json:"number_of_instructors"` PercentStatus map[string]int `json:"percent_status"` } var statuses = []string{"JD", "LLM", "MCL", "PHD", "SJD", "MLS", "SPEC", "MNR", "CERT"} func GenerateUserData (config Config) { // 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", "first_name", "last_name", "status", "exam_id"}); 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) } func GenerateSectionData(config Config) { // Generate unique instructor names: "LastName, X" instructorNames := make([]string, 0, config.NumberOfInstructors) seenInstructors := make(map[string]struct{}, config.NumberOfInstructors) for len(instructorNames) < config.NumberOfInstructors { name := fmt.Sprintf("%s, %c", gofakeit.LastName(), 'A'+rune(rand.Intn(26))) if _, exists := seenInstructors[name]; !exists { seenInstructors[name] = struct{}{} instructorNames = append(instructorNames, name) } } // Generate unique course numbers: one uppercase letter + 3 digits seenCourseNumbers := make(map[string]struct{}, config.NumberOfSections) courseNumbers := make([]string, 0, config.NumberOfSections) for len(courseNumbers) < config.NumberOfSections { cn := fmt.Sprintf("%c%03d", 'A'+rune(rand.Intn(26)), rand.Intn(1000)) if _, exists := seenCourseNumbers[cn]; !exists { seenCourseNumbers[cn] = struct{}{} courseNumbers = append(courseNumbers, cn) } } sectionIDs := uniqueNumericStrings(5, config.NumberOfSections) sections := make([]Section, config.NumberOfSections) for i := range sections { sections[i] = Section{ SectionID: sectionIDs[i], CourseNumber: courseNumbers[i], Instructor: instructorNames[rand.Intn(len(instructorNames))], CourseTitle: gofakeit.BookTitle(), CourseType: course_type[rand.Intn(len(course_type))], } } f, err := os.Create("sections.csv") if err != nil { log.Fatalf("failed to create sections.csv: %v", err) } defer f.Close() w := csv.NewWriter(f) if err := w.Write([]string{"section_id", "course_number", "course_instructor", "course_title", "course_type"}); err != nil { log.Fatalf("failed to write csv header: %v", err) } for _, s := range sections { if err := w.Write([]string{s.SectionID, s.CourseNumber, s.Instructor, s.CourseTitle, s.CourseType}); 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 sections to sections.csv\n", config.NumberOfSections) } 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) } GenerateUserData(config) GenerateSectionData(config) } // 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 }