Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Overview SagaciousMedia is a (fictitious) company that develops educational hard

ID: 3756927 • Letter: O

Question

Overview

SagaciousMedia is a (fictitious) company that develops educational hardware, software, and content for both the formal and informal education markets. SagaciousMedia's products are designed to excite and educate students, and to inspire and assist teachers/instructors.

They are in the process of developing a suite of products for working with grading assignments of various kinds (e.g., exams, homework assignments, labs, programming assignments). They have hired you to construct several interfaces/classes that will, ultimately, become part of these products.

These classes/interfaces might be used by the first application they are creating (called Gradient) to calculate the numerical grade for a course that has 6 programming assignments (with the lowest dropped) that account for 40% of the course grade, 5 homework assignments that account for 10% of the course grade, one midterm exam that accounts for 20% of the course grade, and a final exam that accounts for 30% of the course grade. For example, assuming the the programming assignments and homework assignments are each graded on a 20-point scale and the exams are each graded on a 100-point scale, the following grades

20.0 18.0 5.0 15.0 20.0 20.0 20.0 5.0 0.0 10.0 15.0 80.0 75.0

should result in a PA grade of 20.0+18.0+15.0+20.0+20.0=93.0, a homework grade of 20.0+5.0+0.0+10.0+15.0=50.0 and a course grade of 0.493.0+0.150.0+0.280.0+0.375.0=37.2+5.0+16.0+22.5=80.7

package app;
import grading.*;
import java.util.*;

/**
* An application for calculating the numeric grade for
* a course from the grades on individual assignments.
*
* This version assumes that the course is structured as follows:
*
*   6 programming assignments (PAs) accounting for 40% of the course grade
*   after the lowest grade is dropped.
*  
*   5 homework assignments (HWs) accounting for 10% of the course grade.
*  
*   1 mid-term exam (Midterm) accounting for 20% of the course grade.
*  
*   1 final exam (Final) accounting for 30% of the course grade.
*
* @version 1.0
* @author Sagacious Media
*
*/
public class Gradient
{
  /**
    * The entry point for the application.
    *
    * @param args The 13 grades (ordered as above). Missing grades can be entered as "NA".
    */
   public static void main(String[] args)
   {
      Filter                   paFilter;       
      Grade                    courseGrade, hwGrade, paGrade;
      GradingStrategy          courseStrategy, hwStrategy, paStrategy;
      List<Grade>              grades, hws, pas;
      Map<String, Double>      courseWeights;

     // Early exit
      if ((args == null) || (args.length != 13))
      {
         System.err.println("You must enter all 13 grades. (Use NA for missing.)");
         System.exit(1);
      }

     // Create the filter and strategy for PAs
      paFilter   = new DropFilter(true, false);
      paStrategy =new TotalStrategy();

     // Create the strategy for HWs
      hwStrategy = new TotalStrategy();

     // Create the weights and strategy for the course grade
      courseWeights = new HashMap<String, Double>();
      courseWeights.put("PAs",     0.4);
      courseWeights.put("HWs",     0.1);
      courseWeights.put("Midterm", 0.2);
      courseWeights.put("Final",   0.3);
      courseStrategy =new WeightedTotalStrategy(courseWeights);

     try
      {
        // Put the PA grades in a List
         pas = new ArrayList<Grade>();
        for (int i=0; i<6; i++)
         {
            pas.add(parseGrade("PA"+(i+1), args[i]));
         }

        // Calculate the PA grade (after filtering)
         paGrade = paStrategy.calculate("PAs", paFilter.apply(pas));

        // Put the HW grades in a List
         hws = new ArrayList<Grade>();
        for (int i=0; i<5; i++)
         {
            hws.add(parseGrade("HW"+(i+1), args[i+6]));
         }

        // Calculate the HW grade
         hwGrade = hwStrategy.calculate("HWs", hws);

        // Put all of the grades in a List
         grades = new ArrayList<Grade>();
         grades.add(paGrade);
         grades.add(hwGrade);
         grades.add(parseGrade("Midterm", args[11]));
         grades.add(parseGrade("Final",   args[12]));

        // Calculate the final grade
         courseGrade = courseStrategy.calculate("Course Grade", grades);

        // Display the final grade
         System.out.println(courseGrade.toString());       
      }
     catch (SizeException se)
      {
         System.out.println("You entered too few valid grades.");
    }
     catch (IllegalArgumentException iae)
      {
        // Should never get here since all keys should be valid
      }
   }
  
  /**
    * Construct a Grade object from a key and a String representation of
    * its value. If the String representation of the value is null or not a valid
    * double then the resulting Grade will have a missing value.
    *
    * @param key   The key for the Grade
    * @param value The String representation of the value
    * @return      The corresponding Grade object
    * @throws IllegalArgumentException if the key is invalid
    */
   private static Grade parseGrade(String key, String value) throws IllegalArgumentException
   {
      Grade result;
     
     try
      {
         Double v;
        if (value == null) v = null;
        else v = new Double(Double.parseDouble(value));
        
         result =new Grade(key, v);
      }
     catch (NumberFormatException nfe)
      {
         result =new Grade(key, null);
      }
     
     return result;
   }
}

OVERVIEW:

Specifications: Grade

In addition to the obvious specifications illustrated in the UML class diagram, the Grade class must satisfy the following specifications.

1. Grade objects must be immutable. 2. If a constructor is passed a key that is null or empty (i.e., "") then the constructor must throw an IllegalArgumentException. 3. The Grade(String key) constructor must construct a Grade object with a value attribute of 0.0. 4. The compareTo(Grade other) method must return the result of comparing this.value and other.value accounting for missing (i.e., null) values appropriately. 4.1. If this.value is null and other.value is non-null then it must return -1. 4.2. If this.value is null and other.value is null then it must return 0. 4.3. If this.value is non-null and other.value is null then it must retun 1. 4.4. If both this.value and other.value are non-null then it must return the result of calling compareTo() on this.value and passing it other.value (though it need not be implemented this way). 5. The toString() method must return a String representation of the Grade object. 5.1. If the value attribute is not null then the String must contain the key attribute, followed by the String literal ":", followed by a single space, followed by the value attribute (in a field of width 5 with 1 digit to the right of the decimal point). 5.2. If the value attribute is null then the String must contain the key attribute, followed by by the String literal ":", followed by a single space, followed by the String literal "   NA" (which is also a field of width 5).

Note that, while null key attributes are invalid (i.e., every Grade object must have a non-null, nonempty key attribute), null value attributes are valid (and are used to indicate that the Grade is missing).

Specifications: DropFilter

In addition to the obvious specifications illustrated in the UML class diagram, the DropFilter class must satisfy the following specifications.

public methods must not have any side effects. That is, they must not change the parameters that they are passed in any way (e.g., the List that is passed to the apply() method must not be changed in any way) and they must not change attributes that are not “owned” (i.e., attributes that are aliases) in any way. 2. You may assume that the apply() method is passed a List that does not contain any null elements. 3. The default constructor must construct a DropFilter object that drops the lowest and highest element. 4. The apply() method must construct a new List that is a subset of the List it is passed. 4.1.If the apply() method is passed a List that has an inappropriate size then it must throw a SizeException. 4.1.1. If the apply() method is passed a null List then it must throw a SizeException. 4.1.2. If the apply() method is passed a List that contains fewer elements than are to be dropped then it must throw a SizeException. 4.1.3. If the apply() method is passed a List that contains the same number of elements as are to be dropped then it must throw a SizeException. 4.2.If the apply() method is passed a list that has an appropriate size then it must return a new List. 4.2.1. The elements of the new List must be aliases for (not copies of) the Grade objects in the List it is passed. 4.2.2. Because each Grade object in the List has a key that can be used to identify it, the new List need not be in the same order as the List it is passed. 4.2.3. The elements in (and size) of the returned List must be based on the values of the parameters that were passed to the constructor when the object was constructed. 4.2.3.1. If shouldDropLowest was true then it must drop exactly one of the elements with the lowest value in the original List. 4.2.3.2. If shouldDropHighest was true then it must drop exactly one of the elements with the highest value in the original List. 4.2.3.3. When dropping the highest and lowest, two elements must always be dropped, even if the highest and lowest have the same value. 4.2.3.4. When determining the highest and/or lowest values it must account for missing (i.e., null) values as in the compareTo() method of the Grade class (i.e., missing values have smaller magnitude than non-missing values and one missing value has the same magnitude as another missing value).

Specifications: Missing

The Missing class is a utility class. In addition to the obvious specifications illustrated in the UML class diagram, the Missing class must satisfy the following specifications.

The default double value of a missing value must be 0.0. 2. The method doubleValue(Double number) must return the double value of the Double parameter unless it is null, in which case it must return the default double value of a missing value. 3. The method doubleValue(Double number, double missingValue) must return the double value of the Double parameter unless it is null, in which case it must return missingValue.

Specifications: SizeException

In addition to the obvious specifications illustrated in the UML class diagram, the SizeException class must satisfy the following specifications.

The SizeException class must be a checked exception.

Specifications: TotalStrategy

In addition to the obvious specifications illustrated in the UML class diagram and the specifications for the parent class and interface, the TotalStrategy class must satisfy the following specifications.

public methods must not have any side effects. That is, they must not change the parameters that they are passed in any way (e.g., the List that is passed to the calculate() method must not be changed in any way) and they must not change attributes that are not “owned” (i.e., attributes that are aliases) in any way. 2. The calculate() method must calculate the total of the List of Grade objects it is passed. 2.1.You may assume that the calculate() method is passed a List that does not contain any null elements. 2.2.If the List is null then it must throw a SizeException. 2.3.If the List is empty then it must throw a SizeException. 2.4.Otherwise, it must return a Grade object with the given key and a value equal to the total of the Grade objects in the List. 2.4.1. If the value of a particular Grade is missing (i.e., null) then a value of 0.0 must be used. Note: The Missing class has a method that can be used to accomplish this.

Specifications: WeightedTotalStrategy

In addition to the obvious specifications illustrated in the UML class diagram and the specifications for the parent class and interface, the WeightedTotalStrategy class must satisfy the following specifications.

public methods must not have any side effects. That is, they must not change the parameters that they are passed in any way (e.g., the List that is passed to the calculate() method must not be changed in any way) and they must not change attributes that are not “owned” (i.e., attributes that are aliases) in any way (e.g., the Map that is passed to the constructor must not be changed in any way). 2. The calculate() method must calculate the weighted total of the List of Grade objects it is passed. 2.1.You may assume that the calculate() method is passed a List that does not contain any null elements. 2.2.If the List is null then it must throw a SizeException. 2.3.If the List is empty then it must throw a SizeException. 2.4.Otherwise, it must return a Grade object with the given key and a value equal to the weighted total of the Grade objects in the List. 2.4.1. The weight for each element must be obtained from the Map using the key for that element. 2.4.1.1. If the weights Map is null than a weight of 1.0 must be used. 2.4.1.2. If the weight for a particular Grade is unspecified (i.e., null) then a weight of 1.0 must be used. Note: The Missing class has a method that can be used to accomplish this. 2.4.1.3. If the weight for a particular Grade is less than 0.0 then a weight of 0.0 must be used. 2.4.2. If the value of a particular Grade is missing (i.e., null) then a value of 0.0 must be used. Note: The Missing class has a method that can be used to accomplish this. 3. The default constructor must (directly or indirectly) initialize the weights Map to null.

Product Domain Glossary

Category A collection of graded assessments of the same type. For example, “Homeworks” is a category in many courses. Another example of a category is a “Quiz”. The grades on the assessments in a category are often used to calculate a summary grade for that category.

Course Grade The numeric grade for an entire course. A course grade is often calculated by weighting the summary grades for the categories.

Dropped A grade is said to be dropped when it is not included in subsequent calculations. For example, it is not uncommon to assign 11 homework assignments and then drop the one with the lowest grade.

Grade A labeled numeric score on an assessment mechanism (e.g., an exam) or group of assessment mechanisms (e.g., the homework assignments for the semester). The label is referred to as the key and the numeric score is referred to as the value.

Missing Grade A grade that does not have a numeric score.

Unspecified Weight A weight is said to be unspecified for a particular grade when there is no weight that corresponds to that grade’s label/key.

Weight A number that will be multiplied by the numeric score of a grade. Weights are identified using the label/key of the grade.

Weighted Average The weighted average of a collection of grades is defined as:

=

1 =0 1 =0

where denotes the value of grade (properly accounting for missing values) and denotes the weight associated with grade (properly accounting for unspecified weights).

Weighted Total The weighted total of a collection of grades is defined as:

= 1 =0

where denotes the value of grade (properly accounting for missing values) and denotes the weight associated with grade (properly accounting for unspecified weights). Note that the weighted total and weighted average are identical if the weights are non-negative and sum to 1.

Document:

Epics

E1. As a user I want to be able to enter the numeric grades for the assignments in a course and have the system calculate my numeric grade for the course so that I don’t have to perform the calculations by hand.

E2. As a salesperson I want the system to be able to handle courses with arbitrary structures so that we can sell the system to everyone.

E3. As an administrator I want to be able to enter information that describes the structure of a course so that it can be used for different courses.

Sprintable Stories ID

Story

Acceptance Criteria

S1 As a salesperson I want the system to work for a course with 6 programming assignments where the lowest is dropped (40% of the course grade), 5 homework assignments (10% of the course grade), one mid-term exam (20% of the course grade), and one final exam (30% of the course grade) so that the system can be used right away in several courses offered by one of our perspective clients.

Calculation of correct result for three integration tests with no missing values.

S2 As a salesperson I want the grade for a category to be the total of the grades on the assignments in that category so that the system can be used right away in several courses offered by one of our perspective clients.

Calculation of correct result for three integration tests with no missing values.

S3 As a user I want the system to be able handle missing grades so that I can use it while a course is underway.

Calculation of correct result for one integration test with one missing value in one category.

Calculation of correct result for one integration test with one missing value in each category.

Calculation of correct result for one integration test with multiple missing values in each category.

Calculation of correct result for one integration test with all missing values.

S4 As a user I want to be able to enter all of the grades for a course at the command line so that the system is easy to use.

Calculation of correct result for three system tests with no missing values.

S5 As a user I want to be able to enter missing grades as “NA” so that they can be easily distinguished from actual grades.

Calculation of correct result for one system test with one missing value in one category entered as “NA” from the command line.

Calculation of correct result for one system test with one missing value in each category entered as “NA” from the command line.

Calculation of correct result for one system test with multiple missing values in each category entered as “NA” from the command line.

Calculation of correct result for one system test with all missing values.

S6 As a user I want the system to display the course grade after I enter all of the grades at the command line course so that I don’t have to perform the calculations by hand.

Display of correct output for three system tests with no missing values.

Display of correct output for one system test with one missing value in one category entered as “NA” from the command line.

Display of correct output for one system test with one missing value in each category entered as “NA” from the command line.

Display of correct output for one system test with multiple missing values in each category entered as “NA” from the command line.

Display of correct result for one system test with all missing values.

Tasks

Tasks Stories Related Documents/Notes

1 Identify the signature for a method for calculating a weighted total

S1

2 Organize the method in task 1 into a class

S1 The specifications for the WeightedTotalStrategy class

3 Determine how to handle missing weights

S1 Specification 2.4.1 for the WeightedTotalStrategy class

4 Create unit tests for the class in task 2

S1

5 Test and debug the class in task 2

S1

6 Identify the signature for a method for dropping the lowest grade

S1

7 Organize the method in task 6 into a class

S1 The specifications for the DropFilter class

8 Determine how to handle size issues

S1 Specification 4 for the DropFilter class and the specifications for the SizeException class

9 Create unit tests for the class in task 7

S1

10 Test and debug the class in task 7

S1

11 Identify the signature for a method for calculating a total

S2

12 Organize the method in task 1 into a class

S2 The specifications for the TotalStrategy class

13 Determine how to handle missing weights

S2 Specification 2.4.1 for the TotalStrategy class

14 Create unit tests for the class in task 12

S2

15 Test and debug the class in task 12

S2

16 Design an approach for representing nonmissing values that can be associated with a weight

S3 UML diagram for the Grade class and specification 1 of the Grade class

17 Design an approach for representing missing values that can be associated with a weight

S3 Specifications 2-4 of the Grade class

18 Create unit tests for the class in tasks 16-17

S3

19 Test and debug the class in task 16-17

S3

20 Design an approach for manipulating missing values numerically

S3 UML diagram and specifications for the Missing class

21 Create unit tests for the class in task 20

S3

22 Test and debug the class in task 20

S3

23 Organize all of the classes in the system

UML class diagram of the system

24 Implement the code that accesses the command line arguments

S4

25 Implement the code that converts “NA” arguments to missing values

S5

26 Implement the code that calculates and displays the course grade

S6

27 Create three tests with no missing values

S1, S2, S4, S6

Tests: Complete 01, Complete 02, Complete 03

28 Create test with one missing value in one category

S3, S5, S6

Test: Missing One in One Category

29 Create test with one missing value in each category

S3, S5, S6

Test: Missing One in Each Category

30 Create test with multiple missing values in each category

S3, S5, S6

Test: Missing Multiple in Each Category

31 Create test with all missing values S3, S5, S6

Test: Missing All

32 Create Eclipse “Run Configurations” for each test

S1-S6

33 Test the system

S1-S6 Note: Since the Gradient class has already been completed, integration testing will not be conducted. Instead, the system tests will be used for both purposes.

34 Debug the system (if necessary)

S1-S6

Explanation / Answer

Gradient.java


package app;
import grading.*;
import java.util.*;

public class Gradient
{
   /**
   * The entry point for the application.
   *
   * @param args The 13 grades (ordered as above). Missing grades can be entered as "NA".
   */
   public static void main(String[] args)
   {
       Filter                   paFilter;      
       Grade                    courseGrade, hwGrade, paGrade;
       GradingStrategy          courseStrategy, hwStrategy, paStrategy;
       List<Grade>              grades, hws, pas;
       Map<String, Double>      courseWeights;

       // Early exit
       if ((args == null) || (args.length != 13))
       {
           System.err.println("You must enter all 13 grades. (Use NA for missing.)");
           System.exit(1);
       }

       // Create the filter and strategy for PAs
       paFilter   = new DropFilter(true, false);
       paStrategy = new TotalStrategy();

       // Create the strategy for HWs
       hwStrategy = new TotalStrategy();

       // Create the weights and strategy for the course grade
       courseWeights = new HashMap<String, Double>();
       courseWeights.put("PAs",     0.4);
       courseWeights.put("HWs",     0.1);
       courseWeights.put("Midterm", 0.2);
       courseWeights.put("Final",   0.3);
       courseStrategy = new WeightedTotalStrategy(courseWeights);

       try
       {
           // Put the PA grades in a List
           pas = new ArrayList<Grade>();
           for (int i=0; i<6; i++)
           {
               pas.add(parseGrade("PA"+(i+1), args[i]));
           }

           // Calculate the PA grade (after filtering)
           paGrade = paStrategy.calculate("PAs", paFilter.apply(pas));

           // Put the HW grades in a List
           hws = new ArrayList<Grade>();
           for (int i=0; i<5; i++)
           {
               hws.add(parseGrade("HW"+(i+1), args[i+6]));
           }

           // Calculate the HW grade
           hwGrade = hwStrategy.calculate("HWs", hws);

           // Put all of the grades in a List
           grades = new ArrayList<Grade>();
           grades.add(paGrade);
           grades.add(hwGrade);
           grades.add(parseGrade("Midterm", args[11]));
           grades.add(parseGrade("Final",   args[12]));

           // Calculate the final grade
           courseGrade = courseStrategy.calculate("Course Grade", grades);

           // Display the final grade
           System.out.println(courseGrade.toString());      
       }
       catch (SizeException se)
       {
           System.out.println("You entered too few valid grades.");
       }
       catch (IllegalArgumentException iae)
       {
           // Should never get here since all keys should be valid
       }
   }
  
   /**
   * Construct a Grade object from a key and a String representation of
   * its value. If the String representation of the value is null or not a valid
   * double then the resulting Grade will have a missing value.
   *
   * @param key   The key for the Grade
   * @param value The String representation of the value
   * @return      The corresponding Grade object
   * @throws IllegalArgumentException if the key is invalid
   */
   private static Grade parseGrade(String key, String value) throws IllegalArgumentException
   {
       Grade result;
      
       try
       {
           Double v;
           if (value == null) v = null;
           else v = new Double(Double.parseDouble(value));
          
           result = new Grade(key, v);
       }
       catch (NumberFormatException nfe)
       {
           result = new Grade(key, null);
       }
      
       return result;
   }
}

TotalStrategy.java
package grading;
import java.util.LinkedList;

public class TotalStrategy extends WeightedTotalStrategy{
   public Grade calculate(String givenKey, LinkedList<Grade> grading) throws SizeException {
       @SuppressWarnings("unused")
       double localWeight = 0.0;
       double average = 0.0;
      
       //Security Check
       if (weight.equals(null)) {
           throw new SizeException("Map of weights given is null");
       } else if(weight.isEmpty()) {
           throw new SizeException("map of wieghts is empty. Size = 0");
       }else {
           for(int i=0 ;i < grading.size(); i++) {
               //if user's key exist in weight map, use key's weight accordingly.
               if (weight.containsKey(grading.get(i).getKey())) {
                   localWeight = weight.get(grading.get(i).getKey());
                   if (localWeight < 0.0)
                   {
                       localWeight = 0;
                   }
               }
               //No weight = assume 1.0// assumes Null is accounted
               else {      
                   localWeight = 1.0;
                   //find a way to use missing class to obtain Missing value
                  
               }
               //Begin Calculating Total(Grade Value * weight)
               if (grading.get(i).getValue() == 0.0) {
                   average += Missing.doubleValue(grading.get(i).getValue());
               } else {
                   average += grading.get(i).getValue() * localWeight;
               }
              
           }
           Grade gradedTotal = new Grade(givenKey,average);
           return gradedTotal;
       }      
   }
}