You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
5.4 KiB
Go

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
}