StudentCsvFormat.java

1
package edu.ucsb.cs156.happiercows.helpers;
2
3
import edu.ucsb.cs156.happiercows.entities.Student;
4
import org.apache.commons.csv.CSVRecord;
5
6
import java.util.List;
7
import java.util.function.BiFunction;
8
9
/**
10
 * Different schools export their class rosters with different CSV column
11
 * layouts. Each format below knows how to recognize its own header row and
12
 * how to map a data row to a {@link Student}.
13
 *
14
 * <p>{@code UCSB_EGRADES} matches the header row exported by UCSB's
15
 * registrar (eGrades); see {@code docs/examples/egrades.csv}.
16
 * {@code CHICO_STATE_ROSTER} matches the Canvas roster export used at Chico
17
 * State (see {@code docs/examples/chico_canvas.csv}) and, along with
18
 * {@code GENERIC}, is included as a worked example of how to add support for
19
 * another school's roster format later (see issue #251); {@code GENERIC} is
20
 * also the format used for courses associated with the "Other" school.
21
 */
22
public enum StudentCsvFormat {
23
    UCSB_EGRADES(
24
            List.of(
25
                    "Enrl Cd", "Perm #", "Grade", "Final Units", "Student Last",
26
                    "Student First Middle", "Quarter", "Course ID", "Section",
27
                    "Meeting Time(s) / Location(s)", "Email", "ClassLevel", "Major1",
28
                    "Major2", "Date/Time", "Pronoun"),
29 1 1. lambda$static$0 : replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lambda$static$0 → KILLED
            (row, courseId) -> Student.builder()
30
                    .perm(row.get(1).trim())
31
                    .lastName(row.get(4).trim())
32
                    .firstMiddleName(row.get(5).trim())
33
                    .email(row.get(10).trim())
34
                    .courseId(courseId)
35
                    .build()),
36
37
    CHICO_STATE_ROSTER(
38
            List.of("Student Name", "Student ID", "Student SIS ID", "Email", "Section Name"),
39
            (row, courseId) -> {
40
                String fullName = row.get(0).trim();
41 1 1. lambda$static$1 : replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lambda$static$1 → KILLED
                return Student.builder()
42
                        .perm(row.get(2).trim())
43
                        .lastName(lastNameFromFullName(fullName))
44
                        .firstMiddleName(firstNameFromFullName(fullName))
45
                        .email(row.get(3).trim())
46
                        .courseId(courseId)
47
                        .build();
48
            }),
49
50
    GENERIC(
51
            List.of("lastName", "firstMiddleName", "email", "perm"),
52 1 1. lambda$static$2 : replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lambda$static$2 → KILLED
            (row, courseId) -> Student.builder()
53
                    .lastName(row.get(0).trim())
54
                    .firstMiddleName(row.get(1).trim())
55
                    .email(row.get(2).trim())
56
                    .perm(row.get(3).trim())
57
                    .courseId(courseId)
58
                    .build());
59
60
    private final List<String> headers;
61
    private final BiFunction<CSVRecord, Long, Student> rowMapper;
62
63
    StudentCsvFormat(List<String> headers, BiFunction<CSVRecord, Long, Student> rowMapper) {
64
        this.headers = headers;
65
        this.rowMapper = rowMapper;
66
    }
67
68
    public List<String> getHeaders() {
69 1 1. getHeaders : replaced return value with Collections.emptyList for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::getHeaders → KILLED
        return headers;
70
    }
71
72
    public boolean matchesHeaders(List<String> candidateHeaders) {
73 1 1. matchesHeaders : negated conditional → KILLED
        if (candidateHeaders.size() != headers.size()) {
74 1 1. matchesHeaders : replaced boolean return with true for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::matchesHeaders → KILLED
            return false;
75
        }
76 2 1. matchesHeaders : changed conditional boundary → KILLED
2. matchesHeaders : negated conditional → KILLED
        for (int i = 0; i < headers.size(); i++) {
77 1 1. matchesHeaders : negated conditional → KILLED
            if (!headers.get(i).equalsIgnoreCase(candidateHeaders.get(i).trim())) {
78 1 1. matchesHeaders : replaced boolean return with true for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::matchesHeaders → KILLED
                return false;
79
            }
80
        }
81 1 1. matchesHeaders : replaced boolean return with false for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::matchesHeaders → KILLED
        return true;
82
    }
83
84
    public Student toStudent(CSVRecord row, Long courseId) {
85 1 1. toStudent : replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::toStudent → KILLED
        return rowMapper.apply(row, courseId);
86
    }
87
88
    /**
89
     * @param candidateHeaders the header row read from an uploaded CSV file
90
     * @return the matching format, or null if no known format matches
91
     */
92
    public static StudentCsvFormat detect(List<String> candidateHeaders) {
93
        for (StudentCsvFormat format : values()) {
94 1 1. detect : negated conditional → KILLED
            if (format.matchesHeaders(candidateHeaders)) {
95 1 1. detect : replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::detect → KILLED
                return format;
96
            }
97
        }
98
        return null;
99
    }
100
101
    /**
102
     * Get everything after the last space in the full name. If the string
103
     * contains no spaces, return the entire input string as the result.
104
     *
105
     * @param fullName the full name, e.g. "Marge Simpson"
106
     * @return best estimate of last name, e.g. "Simpson"
107
     */
108
    static String lastNameFromFullName(String fullName) {
109
        int lastSpaceIndex = fullName.lastIndexOf(" ");
110 1 1. lastNameFromFullName : negated conditional → KILLED
        if (lastSpaceIndex == -1) {
111 1 1. lastNameFromFullName : replaced return value with "" for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lastNameFromFullName → KILLED
            return fullName;
112
        }
113 2 1. lastNameFromFullName : Replaced integer addition with subtraction → KILLED
2. lastNameFromFullName : replaced return value with "" for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lastNameFromFullName → KILLED
        return fullName.substring(lastSpaceIndex + 1).trim();
114
    }
115
116
    /**
117
     * Get everything up to and not including the last space in the full name.
118
     * If the string contains no spaces, return an empty string.
119
     *
120
     * @param fullName the full name, e.g. "Marge Simpson"
121
     * @return best estimate of first (and middle) name, e.g. "Marge"
122
     */
123
    static String firstNameFromFullName(String fullName) {
124
        int lastSpaceIndex = fullName.lastIndexOf(" ");
125 1 1. firstNameFromFullName : negated conditional → KILLED
        if (lastSpaceIndex == -1) {
126
            return "";
127
        }
128 1 1. firstNameFromFullName : replaced return value with "" for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::firstNameFromFullName → KILLED
        return fullName.substring(0, lastSpaceIndex).trim();
129
    }
130
}

Mutations

29

1.1
Location : lambda$static$0
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:ucsb_egrades_maps_row_to_student()]
replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lambda$static$0 → KILLED

41

1.1
Location : lambda$static$1
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:chico_state_roster_handles_a_name_with_no_spaces()]
replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lambda$static$1 → KILLED

52

1.1
Location : lambda$static$2
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:generic_maps_row_to_student()]
replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lambda$static$2 → KILLED

69

1.1
Location : getHeaders
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:getHeaders_returns_the_expected_header_list()]
replaced return value with Collections.emptyList for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::getHeaders → KILLED

73

1.1
Location : matchesHeaders
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:returns_null_when_header_count_is_too_few()]
negated conditional → KILLED

74

1.1
Location : matchesHeaders
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:returns_null_when_header_count_is_too_few()]
replaced boolean return with true for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::matchesHeaders → KILLED

76

1.1
Location : matchesHeaders
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:detects_generic_format()]
changed conditional boundary → KILLED

2.2
Location : matchesHeaders
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:returns_null_when_header_count_is_too_many()]
negated conditional → KILLED

77

1.1
Location : matchesHeaders
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:detects_generic_format()]
negated conditional → KILLED

78

1.1
Location : matchesHeaders
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:returns_null_when_header_count_is_too_many()]
replaced boolean return with true for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::matchesHeaders → KILLED

81

1.1
Location : matchesHeaders
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:detects_generic_format()]
replaced boolean return with false for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::matchesHeaders → KILLED

85

1.1
Location : toStudent
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:chico_state_roster_handles_a_name_with_no_spaces()]
replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::toStudent → KILLED

94

1.1
Location : detect
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:returns_null_when_header_count_is_too_few()]
negated conditional → KILLED

95

1.1
Location : detect
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:detects_generic_format()]
replaced return value with null for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::detect → KILLED

110

1.1
Location : lastNameFromFullName
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:chico_state_roster_maps_row_to_student()]
negated conditional → KILLED

111

1.1
Location : lastNameFromFullName
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:chico_state_roster_handles_a_name_with_no_spaces()]
replaced return value with "" for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lastNameFromFullName → KILLED

113

1.1
Location : lastNameFromFullName
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:chico_state_roster_maps_row_to_student()]
Replaced integer addition with subtraction → KILLED

2.2
Location : lastNameFromFullName
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:chico_state_roster_maps_row_to_student()]
replaced return value with "" for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::lastNameFromFullName → KILLED

125

1.1
Location : firstNameFromFullName
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:chico_state_roster_handles_a_name_with_no_spaces()]
negated conditional → KILLED

128

1.1
Location : firstNameFromFullName
Killed by : edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.happiercows.helpers.StudentCsvFormatTests]/[method:chico_state_roster_maps_row_to_student()]
replaced return value with "" for edu/ucsb/cs156/happiercows/helpers/StudentCsvFormat::firstNameFromFullName → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0