(**** * * Module Graph defines the objects and operations related to displaying * and manipulating course statistics and policies of the Grader. * *) module Graph; from Spreadsheet import Gradesheet; object Graph is components: Title and gLines:GradeLine*; description: (* A Graph is the most generic type of graphical item. The two types of grahical items -- Histogram and PieChart, are specializations of Graph. Graph uses data from Gradesheet. Title is the name of the graph. Each Graph has a list of GradeLines and integers. The list of integers is used to compute the number of scores in the GradeLines. *); end Graph; object Histogram inherits from Graph components: scores:integer*; description: (* A Histogram displays the pattern of variation of class grades. Each row contains zero or more scores. Curving is done by moving the GradeLines up or down. T he list of integers is displayed as markers on the rows. *); end Histogram; object PieChart inherits from Graph description: (* A Piechart displays the comparison of the different groups of student. Each segment represents a grade group. Curving is done by invoking the DragLineUp or DragLineDown operations. *); end PieChart; object GradeLine is components: position:integer and n:NumberOfScores and grade:Grade and percent:Percentage and upper:integer and lower:integer; description: (* GradeLine represents a line that separates the different grade segments. An integer is to keep track of where the GradeLine is on the Histogram. UpperBound and LowerBound prevent the GradeLine from going out of bounds and crossing other Gradelines. IsUp tells which arrow is being selected. UpperBound is defined to be the GradeLine just above the current GradeLine. For the A GradeLine only, the UpperBound is 100. The LowerBound is defined to be the GradeLine just below the current GradeLine. For the F GradeLine only, there is no LowerBound. The F GradeLine is at th lowest score of the Gradesheet. *); end GradeLine; object Title is string description: (* Title is a free form string indicating the type and data the graph is modeled after. *); end Title; object Percentage is integer; object Grade is string; object NumberOfScores is integer; operation DisplayGraph is inputs: gs:Gradesheet; outputs: g:Graph; precondition: (* * There must be at least one student in the Gradesheet *) gs.si != nil; postcondition: (* * Take StudentInfo objects from Gradesheet and count the number of scores * for each GradeLine. Then sum up the list of integers for a Histogram. *) forall (gl in g.gLines) gl.n = SumScores(gs.si, gl.position, gl.upper) and g.gLines[1].position = 90 and g.gLines[1].grade = "A" and g.gLines[1].upper = 100 and g.gLines[1].lower = g.gLines[2].position and g.gLines[2].position = 80 and g.gLines[2].grade = "B" and g.gLines[2].upper = g.gLines[1].position and g.gLines[2].lower = g.gLines[3].position and g.gLines[3].position = 70 and g.gLines[3].grade = "C" and g.gLines[3].upper = g.gLines[2].position and g.gLines[3].lower = g.gLines[4].position and g.gLines[4].position = 60 and g.gLines[4].grade = "D" and g.gLines[4].upper = g.gLines[3].position and g.gLines[4].lower = g.gLines[5].position and g.gLines[5].position = 50 and g.gLines[5].grade = "F" and g.gLines[5].upper = g.gLines[4].position and g.gLines[5].lower = g.gLines[5].position and #g.gLines = 5 and UpdatePercentage(gs, g) and forall (histo:Histogram) forall(i:integer | (i>=1) and (i<=101)) histo.scores[i] = NumberWhoScored(gs.si, i); description: (* Display the Graph. Required a Gradehsheet with at least one student. *); end DisplayGraph; function SumScores(si:Gradesheet.StudentInfo*, lower:integer, upper:integer) = if (si[1].s >= lower and si[1].s <= upper) then (1 + SumScores(si[2:#si], lower, upper)) else SumScores(si[2:#si], lower, upper) end; function NumberWhoScored(si:Gradesheet.StudentInfo*, score:integer) = if (si = nil) then 0 else if (si[1].s = score) then (1 + NumberWhoScored(si[2:#si], score)) else NumberWhoScored(si[2:#si], score) end; function UpdatePercentage(gs:Gradesheet, graph:Graph) = forall (gl:GradeLine | gl in graph.gLines) forall (i:integer | gl != nil) ((gl.percent = (gl.n / #gs.si) * 100) and gl = graph.gLines[i+1]) end; operation DragLineUp inputs: index:integer, destination:integer, g:Graph, gs:Gradesheet; outputs: g':Graph; precondition: (* * Can only move up if there is space between the position and upper. * Index is the index of the gradeline in g.gLines. * The range between the gradeline and its upperbound GradeLine must be * greater than zero. If it is zero, then the gradeline is on top on the upperbound GradeLine. *) ((g.gLines[index].upper - g.gLines[index].position) != 0); postcondition: (* * Update g'.gLines position and recompute the number of scores on each GradeLine. * A gradeline cannot move across another gradeline. *) g'.gLines[index].position = destination and forall (gl in g'.gLines) if gl != nil then gl.n = SumScores(gs.si, gl.position, gl.upper) and UpdatePercentage(gs, g'); description: (* DragLineUp moves the GradeLine up when the up arrow of a GradeLine is clicked and dragged up. *); end DragLineUp; operation DragLineDown inputs: index:integer, destination:integer, g:Graph, gs:Gradesheet; outputs: g':Graph; precondition: (* * Can only move down if there is space between the position and lower. * Index is the index of the gradeline in g.gLines. * The range between the gradeline and its lowerbound GradeLine must be * greater than zero. If it is zero, then the gradeline is on top on the lowerbound GradeLine. *) ((g.gLines[index].position - g.gLines[index].lower) != 0); postcondition: (* * Update g'.gLines position and recompute the number of scores on each GradeLine. * A gradeline cannot move across another gradeline. *) g'.gLines[index].position = destination and forall (gradeline in g'.gLines) if (gradeline != nil) then gradeline.n = SumScores(gs.si, gradeline.position, gradeline.upper) and UpdatePercentage(gs, g'); description: (* DragLineDown moves the GradeLine down when the down arrow of a GradeLine is clicked and dragged down. *); end DragLineDown; end Graph;