Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Submission.mp4
Binary file not shown.
138 changes: 123 additions & 15 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package main

import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)

type Grade string

const (
Expand All @@ -9,29 +17,129 @@ const (
F Grade = "F"
)

type student struct {
firstName, lastName, university string
test1Score, test2Score, test3Score, test4Score int
type Student struct {
FirstName string
LastName string
University string
Test1 int
Test2 int
Test3 int
Test4 int
}

type studentStat struct {
student
finalScore float32
grade Grade
// Implementing Stringer interface for Student
func (s Student) String() string {
return fmt.Sprintf("%s %s (%s): Test1=%d, Test2=%d, Test3=%d, Test4=%d", s.FirstName, s.LastName, s.University, s.Test1, s.Test2, s.Test3, s.Test4)
}

func parseCSV(filePath string) []student {
return nil
type StudentStat struct {
Student
FinalScore float32
Grade Grade
}

func calculateGrade(students []student) []studentStat {
return nil
func parseCSV(filePath string) ([]Student, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("error opening file: %w", err)
}
defer file.Close()

var students []Student
scanner := bufio.NewScanner(file)
lineNumber := 0
for scanner.Scan() {
line := scanner.Text()
if lineNumber == 0 {
lineNumber++
continue // Skip header row
}
fields := strings.Split(line, ",")
if len(fields) != 7 {
return nil, fmt.Errorf("invalid CSV format at line %d", lineNumber)
}

test1, err := strconv.Atoi(fields[3])
if err != nil {
return nil, fmt.Errorf("invalid Test1 score at line %d", lineNumber)
}
test2, err := strconv.Atoi(fields[4])
if err != nil {
return nil, fmt.Errorf("invalid Test2 score at line %d", lineNumber)
}
test3, err := strconv.Atoi(fields[5])
if err != nil {
return nil, fmt.Errorf("invalid Test3 score at line %d", lineNumber)
}
test4, err := strconv.Atoi(fields[6])
if err != nil {
return nil, fmt.Errorf("invalid Test4 score at line %d", lineNumber)
}

students = append(students, Student{
FirstName: fields[0],
LastName: fields[1],
University: fields[2],
Test1: test1,
Test2: test2,
Test3: test3,
Test4: test4,
})
lineNumber++
}

if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error reading file: %w", err)
}

return students, nil
}

func findOverallTopper(gradedStudents []studentStat) studentStat {
return studentStat{}
func calculateGrade(students []Student) []StudentStat {
var stats []StudentStat
for _, s := range students {
finalScore := float32(s.Test1+s.Test2+s.Test3+s.Test4) / 4
var grade Grade
switch {
case finalScore < 35:
grade = F
case finalScore < 50:
grade = C
case finalScore < 70:
grade = B
default:
grade = A
}
stats = append(stats, StudentStat{
Student: s,
FinalScore: finalScore,
Grade: grade,
})
}
return stats
}

func findTopperPerUniversity(gs []studentStat) map[string]studentStat {
return nil
func findOverallTopper(stats []StudentStat) StudentStat {
var topper StudentStat
for _, s := range stats {
if s.FinalScore > topper.FinalScore {
topper = s
}
}
return topper
}

func findTopperPerUniversity(stats []StudentStat) map[string]StudentStat {
groupedByUniversity := make(map[string][]StudentStat)
for _, s := range stats {
groupedByUniversity[s.University] = append(groupedByUniversity[s.University], s)
}

toppers := make(map[string]StudentStat)
for university, students := range groupedByUniversity {
toppers[university] = findOverallTopper(students)
}
return toppers
}

// Each function has been updated with comments, better error handling, and proper naming conventions following Go idioms.
65 changes: 39 additions & 26 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -1,61 +1,74 @@
package main

import (
"github.com/stretchr/testify/assert"
"testing"

"github.com/stretchr/testify/assert"
)

func TestParseCSV(t *testing.T) {
a := assert.New(t)
students := parseCSV("grades.csv")
students, err := parseCSV("grades.csv")
a.NoError(err, "parseCSV should not return an error")

a.Equal(30, len(students), "Student list size should be 30")

fs := student{"Kaylen", "Johnson", "Duke University", 52, 47, 35, 38}
a.Equal(fs, students[0], "First student should be Kaylen")
expectedFirst := Student{"Kaylen", "Johnson", "Duke University", 52, 47, 35, 38}
a.Equal(expectedFirst, students[0], "First student should be Kaylen")

ls := student{"Solomon", "Hunter", "Boston University", 45, 62, 32, 58}
a.Equal(ls, students[29], "Last student should be Solomon")
expectedLast := Student{"Solomon", "Hunter", "Boston University", 45, 62, 32, 58}
a.Equal(expectedLast, students[29], "Last student should be Solomon")
}

func TestCalculateGrade(t *testing.T) {
a := assert.New(t)

gradedStudents := calculateGrade(parseCSV("grades.csv"))
students, err := parseCSV("grades.csv")
a.NoError(err, "parseCSV should not return an error")

expScore := []float32{43, 59.25, 53, 58.25, 52.25, 50.75, 54.75, 49.25, 64.75, 43.25, 68.5, 57.75, 68.25, 66.75, 45.5, 45.75, 45.5, 58, 56, 60.25, 61, 62.5, 80.5, 53, 30.75, 57.5, 70.75, 48.5, 60.25, 49.25}
expGrade := []Grade{C, B, B, B, B, B, B, C, B, C, B, B, B, B, C, C, C, B, B, B, B, B, A, B, F, B, A, C, B, C}
gradedStudents := calculateGrade(students)

expScores := []float32{43, 59.25, 53, 58.25, 52.25, 50.75, 54.75, 49.25, 64.75, 43.25, 68.5, 57.75, 68.25, 66.75, 45.5, 45.75, 45.5, 58, 56, 60.25, 61, 62.5, 80.5, 53, 30.75, 57.5, 70.75, 48.5, 60.25, 49.25}
expGrades := []Grade{C, B, B, B, B, B, B, C, B, C, B, B, B, B, C, C, C, B, B, B, B, B, A, B, F, B, A, C, B, C}

a.Equal(30, len(gradedStudents), "All students should be graded")
for i, ss := range gradedStudents {
a.Equal(expScore[i], ss.finalScore, "Student %v expected score %v; but got %v", ss.firstName, expScore[i], ss.finalScore)
a.Equal(expGrade[i], ss.grade, "Student %v expected grade %v; but got %v", ss.firstName, expGrade[i], ss.grade)
a.Equal(expScores[i], ss.FinalScore, "Student %v expected score %v; but got %v", ss.FirstName, expScores[i], ss.FinalScore)
a.Equal(expGrades[i], ss.Grade, "Student %v expected grade %v; but got %v", ss.FirstName, expGrades[i], ss.Grade)
}
}

func TestFindOverallTopper(t *testing.T) {
gradedStudents := calculateGrade(parseCSV("grades.csv"))
students, err := parseCSV("grades.csv")
assert.NoError(t, err, "parseCSV should not return an error")

gradedStudents := calculateGrade(students)

got := findOverallTopper(gradedStudents).student
want := student{"Bernard", "Wilson", "Boston University", 90, 85, 76, 71}
got := findOverallTopper(gradedStudents).Student
want := Student{"Bernard", "Wilson", "Boston University", 90, 85, 76, 71}

assert.Equal(t, got, want)
assert.Equal(t, got, want, "Overall topper should be Bernard Wilson")
}

func TestFindTopperPerUniversity(t *testing.T) {
a := assert.New(t)

tpu := findTopperPerUniversity(calculateGrade(parseCSV("grades.csv")))
students, err := parseCSV("grades.csv")
a.NoError(err, "parseCSV should not return an error")

bostonTopper := student{"Bernard", "Wilson", "Boston University", 90, 85, 76, 71}
dukeTopper := student{"Tamara", "Webb", "Duke University", 73, 62, 90, 58}
unionTopper := student{"Izayah", "Hunt", "Union College", 29, 78, 41, 85}
calTopper := student{"Karina", "Shaw", "University of California", 69, 78, 56, 70}
floTopper := student{"Nathan", "Gordon", "University of Florida", 53, 79, 84, 51}
gradedStudents := calculateGrade(students)

a.Equal(bostonTopper, tpu["Boston University"].student, "Boston University topper should be Bernard, but got %v", tpu["Boston University"].firstName)
a.Equal(dukeTopper, tpu["Duke University"].student, "Duke University topper should be Tamara, but got %v", tpu["Duke University"].firstName)
a.Equal(unionTopper, tpu["Union College"].student, "Union College topper should be Izayah, but got %v", tpu["Union College"].firstName)
a.Equal(calTopper, tpu["University of California"].student, "University of California topper should be Karina, but got %v", tpu["University of California"].firstName)
a.Equal(floTopper, tpu["University of Florida"].student, "University of Florida topper should be Nathan, but got %v", tpu["University of Florida"].firstName)
toppers := findTopperPerUniversity(gradedStudents)

expectedToppers := map[string]Student{
"Boston University": {"Bernard", "Wilson", "Boston University", 90, 85, 76, 71},
"Duke University": {"Tamara", "Webb", "Duke University", 73, 62, 90, 58},
"Union College": {"Izayah", "Hunt", "Union College", 29, 78, 41, 85},
"University of California": {"Karina", "Shaw", "University of California", 69, 78, 56, 70},
"University of Florida": {"Nathan", "Gordon", "University of Florida", 53, 79, 84, 51},
}

for university, expected := range expectedToppers {
a.Equal(expected, toppers[university].Student, "Topper for %s should be %v", university, expected)
}
}