package objects; import java.util.Map; import java.util.HashMap; import java.util.Collection; import java.util.List; import java.util.ArrayList; import java.util.Random; import java.math.BigDecimal; /** * Object representing an assignment for a course * * @author Jesse W. Milburn * @date 01 October, 2013 */ public class Assignment { private String name; private int worth; private Map<String, Integer> grades = new HashMap<String, Integer>(); private Map<String, Integer> studentGrades = new HashMap<String, Integer>(); public Assignment() { } /** * Creates an Assignment object * * @param an Name of the assignment * @param val Maximum point value of the assignment */ public Assignment(String an, int val) { name = an; worth = val; } /** * Sets a name to the name field * * @param an Sets the name of the assignment */ public void setName(String an) { name = an; } /** * Sets the maximum point value for the assignment * * @param val Maximum point value of the assignment */ public void setWorth(int val) { worth = val; } /** * Fetches the average grade for one assignment for students only * does not ascertain the ghost students scores at all * * @param students List of Student objects * @return the average grade for the assignment */ public double getAssignmentAverageGrade(List<Student> students){ double studentPoints = getTotalStudentPoints(students); double maximumPoints = getMaximumPoints(students); return ((studentPoints / maximumPoints) * 100); } /** * Fetches the average point value for one assignment for students only * does not ascertain the ghost students scores at all * * @param students List of Student objects * @return the average grade for the assignment */ public double getAssignmentAveragePoints(List<Student> students){ double studentPoints = getTotalStudentPoints(students); return (studentPoints / students.size()); } public int getTotalStudentPoints(List<Student> students) { int total = 0; for (int i = 0; i < students.size(); i++) { if (grades.get(students.get(i).getPseudoName()) != null) total += grades.get(students.get(i).getPseudoName()); } return total; } public int getMaximumPoints(List<Student> students) { return (worth * students.size()); } /** * Fetches the name field of the assignment * * @return The title of the assignment */ public String getName() { return name; } /** * Fetches the maximum point value for the assignment * * @return Maximum point value of the assignment */ public int getWorth() { return worth; } /** * Sets the grade for the student by the students pseudo name * * @param pseudoName The pseudo name of the student * @param grade The score the student is being assigned for the assignment * @return The previous value associated to the key. */ public Integer setGrade(String pseudoName, Integer grade, boolean student) { grades.put(pseudoName, grade); if (student && grade != null) { studentGrades.put(pseudoName, grade); } return grades.get(pseudoName); } /** * Gets the grade for the requested student * * @param pseudoName The pseudo name of the student * @return Integer value representing the students grade. Returns * null if the mapping is empty, or if the pseudo name * does not exist as a key. */ public Integer getGrade(String pseudoName) { return grades.get(pseudoName); } /** * Gets an ArrayList of all the values in the grades HashMap * Useful for dealing with statistical analysis * * @return Collection of values in the grades HashMap */ public ArrayList<Integer> getAllGrades() { return new ArrayList<Integer>(grades.values()); } /** * Gets an ArrayList of all the values in the studentGrades HashMap * Useful for dealing with statistical analysis * * @return Collection of values in the studentGrades HashMap */ public ArrayList<Integer> getAllStudentGrades() { return new ArrayList<Integer>(studentGrades.values()); } private boolean notYetEntered() { ArrayList<Integer> sgList = getAllStudentGrades(); for (int i=0; i < sgList.size(); i++) { if (sgList.get(i) != null) { return false; } } return true; } public void setGhostGrades(String[] ghostNames) { if (notYetEntered()) { for (int i = 0; i < ghostNames.length; i++) { this.setGrade(ghostNames[i], null, false); } } else { int[] ghostGrades = new int[ghostNames.length]; assignGhostGrades(ghostGrades); for (int i = 0; i < ghostNames.length; i++) { if (grades.get(ghostNames[i]) == null) { this.setGrade(ghostNames[i], ghostGrades[i], false); } } } } private void assignGhostGrades(int[] ghostGrades) { ArrayList<Integer> sgList = getAllStudentGrades(); int numberOfGhosts = ghostGrades.length; int numberOfStudents = sgList.size(); int startIndex = 0; int endIndex= 1; int[] numberOfScoresInRanges = new int[numberOfRanges(worth)];//this needs error handling int[][] ranges = defineRanges(worth); numberOfScoresInRanges = numberOfScoresInRanges(sgList, ranges, numberOfScoresInRanges.length); double studentsMean = mean(sgList); Random rng = new Random(); int offset = 0;//tracks the number of scores assigned do { for (int i = 0; i < ranges.length; i++) {//wrap entire loop in a do while or call outside functions double percentage = (double)numberOfScoresInRanges[i] / (double)numberOfStudents; int amountOfGhosts = (int)Math.round(numberOfGhosts * percentage); int randomMultiplier = ranges[i][endIndex] - ranges[i][startIndex]; //inserts scores into the ghostGrades array by percentages of students scoring in ranges if (percentage > 0.0) { for (int j = 0; j < amountOfGhosts; j++) { if (offset + j == ghostGrades.length) break;//if percentages assigned more than allowed break out ghostGrades[j + offset] = rng.nextInt(randomMultiplier + 1) + ranges[i][startIndex]; } } offset += amountOfGhosts; //checks at end of foor loop for end case #2 having one fewer score assigned from percentage mismatch if (offset < numberOfGhosts && i == ranges.length - 1) { ghostGrades[numberOfGhosts - 1] = (int)studentsMean;//cheap fix doesn't verify a student //has a grade in the median range } } } while (offset < numberOfGhosts-1); //if means do not match this adjust the grades one by one until means match adjustGhostGradesToMean(ghostGrades, studentsMean, worth); //randomize ghost grades in array so not in order shuffleArray(ghostGrades); } private void adjustGhostGradesToMean(int[] ghostGrades, double mean, int worth) { Random rng = new Random(); int precision = 1; double studentsMean = round(mean, precision, BigDecimal.ROUND_HALF_UP); double ghostsMean = round(mean(ghostGrades), precision, BigDecimal.ROUND_HALF_UP); while (studentsMean != ghostsMean) { int findScoreIndex = rng.nextInt(ghostGrades.length); if (studentsMean < ghostsMean && ghostGrades[findScoreIndex] > studentsMean) { ghostGrades[findScoreIndex]--; ghostsMean = round(mean(ghostGrades), precision, BigDecimal.ROUND_HALF_UP); } else if (studentsMean > ghostsMean && ghostGrades[findScoreIndex] < studentsMean && ghostGrades[findScoreIndex] < worth) { ghostGrades[findScoreIndex]++; ghostsMean = round(mean(ghostGrades), precision, BigDecimal.ROUND_HALF_UP); } } } private double round(double unrounded, int precision, int roundingMode) { String number = Double.toString(unrounded); BigDecimal bd = new BigDecimal(number); BigDecimal rounded = bd.setScale(1, roundingMode); return rounded.doubleValue(); } private void shuffleArray(int[] array) { Random rng = new Random(); for (int i = 0; i < array.length; i++) { int index = rng.nextInt(i + 1); int temp = array[index]; array[index] = array[i]; array[i] = temp; } } private int[] numberOfScoresInRanges(ArrayList<Integer> studentsGrades, int[][] ranges, int size) { int[] numberOfScoresInRanges = new int[size]; for (int i = 0; i < studentsGrades.size(); i++) { Integer currentStudentGrade = studentsGrades.get(i); if (currentStudentGrade != null) { for (int j = 0; j < ranges.length; j++) { if (currentStudentGrade >= ranges[j][0] && currentStudentGrade <= ranges[j][1]) { numberOfScoresInRanges[j] ++; break;//if added breaks out of inner loop } } } } return numberOfScoresInRanges; } private int[][] defineRanges(int worth) { int numberOfRanges = numberOfRanges(worth); int[][] ranges = new int[numberOfRanges][2]; //set fields for setting ranges algorithm int start = 0;//index 0 holds the start of the range int end = 1;//index 1 holds the end of the range int baseRange = worth / (numberOfRanges - 1);//the first range is 0 - 0 int numberOfRangesWithMoreThanBase = worth % (numberOfRanges - 1);//first range is 0 - 0 int startingRangeWithMore = numberOfRanges - numberOfRangesWithMoreThanBase; int current = 1; for (int i = 1; i < ranges.length; i++) { ranges[i][start] = current; current += (baseRange - 1); ranges[i][end] = current; if (i >= startingRangeWithMore) { current ++; ranges[i][end] = current; } current++; } return ranges; } private int numberOfRanges(int worth) { int [] RANGES = {-1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10}; if (worth > 18) return 11;//maximum number of ranges else if (worth <= 0) return -1;//or throw an error return RANGES[worth]; } public double mean(ArrayList<Integer> values) { double temp = 0.0; for (int i = 0; i < values.size(); i++) { Integer val = values.get(i); if (val != null) { temp += val; } } return temp / values.size(); } public double mean(int[] values) { double temp = 0.0; for (int i = 0; i < values.length; i++) { temp += values[i]; } return temp / values.length; } }