Curve-O-Matic
<< Determinant | OtherProjectsTrailIndex | State Applet >>
The runnable jar file is here: CurveOMatic.jar A Mac version is here : http://www.mathorama.com/Curve-O-Matic.dmg
CurveOMatic.java
import java.awt.*;
import java.awt.event.*;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
import javax.swing.*;
@SuppressWarnings("serial")
public class CurveOMatic extends JFrame implements ActionListener
{
private Button openButton, curveButton, sortButton;
private Button saveGQButton, showGQButton;
Panel centerPanel, listPanel, titlePanel;
JScrollPane sp;
BarChart chart;
ArrayList<LineItem> names;
TextArea message;
JTextField raw1=new JTextField();
JTextField raw2=new JTextField();
JTextField curve1=new JTextField();
JTextField curve2=new JTextField();
int r1,r2,c1,c2;
String curveExplanation="";
final JComponent[] inputs = new JComponent[] {
new JLabel("Map raw score "),
raw1,
new JLabel("to "),
curve1,
new JLabel("Map raw score "),
raw2,
new JLabel("to "),
curve2
};
public CurveOMatic(){
super("Curve-O-Matic");
this.setSize(800,600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
r1=1; c1=1;
r2=100; c2=100;
//add buttons and such
message = new TextArea("Tab to next score, Enter to update\nStudent File format: ID, Last, First");
message.setPreferredSize(new Dimension (this.getWidth(),80));
this.setLayout(new BorderLayout());
this.add(message, BorderLayout.SOUTH);
openButton = new Button("Load Student File");
openButton.addActionListener(this);
curveButton = new Button("Set Curve");
curveButton.addActionListener(this);
sortButton = new Button("Sort");
sortButton.addActionListener(this);
saveGQButton = new Button("Save GQ File");
saveGQButton.addActionListener(this);
showGQButton = new Button("Show GQ File");
showGQButton.addActionListener(this);
Panel north = new Panel(new FlowLayout());
north.add(openButton);
north.add(sortButton);
north.add(curveButton);
north.add(showGQButton);
north.add(saveGQButton);
this.add(north, BorderLayout.NORTH);
listPanel=new Panel(new GridLayout(0,1));
names = new ArrayList<LineItem>();
for (int i=1; i<=32;i++){
Student s = new Student(i, " Name "+i+" ");
LineItem item=new LineItem(s, this);
names.add(item);
Panel linePanel=new Panel(new FlowLayout(FlowLayout.LEFT));
linePanel.add(item.getNameLabel());
linePanel.add(item.getRawScoreField());
linePanel.add(item.getCurveScore());
linePanel.setPreferredSize(new Dimension(370, 23));
listPanel.add(linePanel);
}
sp = new JScrollPane(listPanel);
centerPanel=new Panel(new GridLayout(0,2));
centerPanel.add(sp);
chart = new BarChart(names);
centerPanel.add(chart);
add(centerPanel, BorderLayout.CENTER);
}
public static void main(String[] args)
{
CurveOMatic app= new CurveOMatic();
app.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource()==openButton ){
try{
readFile();
}catch(IOException ex){
}
}else if (e.getSource()==sortButton){
int sortBy=sortDialog();
if (sortBy>0){
doSort(sortBy);
}
}else if (e.getSource()==curveButton){
JOptionPane.showMessageDialog(null, inputs, "Set Curve", JOptionPane.PLAIN_MESSAGE);
try{
r1=Integer.parseInt(raw1.getText());
r2=Integer.parseInt(raw2.getText());
c1=Integer.parseInt(curve1.getText());
c2=Integer.parseInt(curve2.getText());
setCurve();
chart.repaint();
}catch( Exception exception){
}
}else if (e.getSource()==showGQButton){
showGQDialog();
}else if (e.getSource()==saveGQButton){
saveGQDialog();
}else { //User typed a return while changing a raw score
for(int i=0;i<names.size();i++){
names.get(i).updateRawScore();
}
setCurve();
chart.repaint();
//System.out.println(e.getActionCommand()+" ");
}
}
private void doSort(int sortBy) {
if (sortBy==LineItem.NamesAscending)
Collections.sort(names);
if (sortBy==LineItem.NamesDescending){
Comparator<LineItem> comp = Collections.reverseOrder();
Collections.sort(names, comp);
}
if (sortBy==LineItem.ScoresAsecending)
Collections.sort(names, LineItem.ScoreComparator);
if (sortBy==LineItem.ScoresDescending){
Comparator<LineItem> comp = Collections.reverseOrder(LineItem.ScoreComparator);
Collections.sort(names, comp);
}
listPanel=new Panel(new GridLayout(0,1));
for (int i=0; i<names.size();i++){
Panel linePanel=new Panel(new FlowLayout(FlowLayout.LEFT));
linePanel.add(names.get(i).getNameLabel());
linePanel.add(names.get(i).getRawScoreField());
linePanel.add(names.get(i).getCurveScore());
linePanel.setPreferredSize(new Dimension(370, 23));
listPanel.add(linePanel);
}
sp = new JScrollPane(listPanel);
remove(centerPanel);
centerPanel=new Panel(new GridLayout(0,2));
centerPanel.add(sp);
setCurve();
chart = new BarChart(names);
centerPanel.add(chart);
add(centerPanel, BorderLayout.CENTER);
validate();
}
public void readFile()throws IOException{
String path = new java.io.File(".").getCanonicalPath();
path=System.getProperty("user.dir");
JFileChooser chooser = new JFileChooser(path);
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION)
{
names = new ArrayList<LineItem>();
FileReader reader = new FileReader(chooser.getSelectedFile());
Scanner fileIn = new Scanner(reader);
listPanel=new Panel(new GridLayout(0,1));
while (fileIn.hasNextLine()){
String line=fileIn.nextLine();
String [] args=line.split(",");
Student s=new Student(args);
LineItem item = new LineItem(s, this);
names.add(item);
Panel linePanel=new Panel(new FlowLayout(FlowLayout.LEFT));
linePanel.add(item.getNameLabel());
linePanel.add(item.getRawScoreField());
linePanel.add(item.getCurveScore());
linePanel.setPreferredSize(new Dimension(370, 23));
listPanel.add(linePanel);
}
fileIn.close();
sp = new JScrollPane(listPanel);
remove(centerPanel);
centerPanel=new Panel(new GridLayout(0,2));
centerPanel.add(sp);
setCurve();
chart = new BarChart(names);
centerPanel.add(chart);
add(centerPanel, BorderLayout.CENTER);
validate();
}
}
public void setCurve(){
if (r2-r1==0){
message.append( "\nDivision by zero - Try again");
return;
}
int rise=c2-c1;
int run=r2-r1;
Rational slope=new Rational(rise,run);
curveExplanation= "Mapping "+r1+" --> "+c1+" and "+r2+" --> "+c2+"\r\n";
curveExplanation+="Curve = ("+slope.toString()+")(raw - "+r2+") + "+c2;
message.setText(curveExplanation);
for(int i=0; i<names.size();i++){
int r=names.get(i).getStudent().getRaw();
int c=(int)(c2+slope.value()*(r-r2));
if (r>=0){
names.get(i).getStudent().setCurve(c);
names.get(i).updateCurveScore();
}else {
names.get(i).getStudent().setCurve(-1);
names.get(i).updateCurveScore();
}
}
validate();
}
public int sortDialog(){
JRadioButton na = new JRadioButton("<html><font color=black>Names, Ascending</font></html>");
JRadioButton nd = new JRadioButton("<html><font color=black>Names, Descending</font></html>");
JRadioButton sa = new JRadioButton("<html><font color=blue>Scores, Ascending</font></html>");
JRadioButton sd = new JRadioButton("<html><font color=blue>Scores, Descending</font></html>");
na.setSelected(true);
ButtonGroup group = new ButtonGroup();
group.add(na);
group.add(nd);
group.add(sa);
group.add(sd);
Object[] array = {
new JLabel("Sort by:"),
na,
nd,
sa,
sd
};
int res = JOptionPane.showConfirmDialog(null, array, "Select",
JOptionPane.OK_CANCEL_OPTION);
if (res == JOptionPane.CANCEL_OPTION) return 0;
if (na.isSelected()) return LineItem.NamesAscending;
if (nd.isSelected()) return LineItem.NamesDescending;
if (sa.isSelected()) return LineItem.ScoresAsecending;
return LineItem.ScoresDescending;
}
public void showGQDialog(){
String nameMessage="The output below is in the format GQ uses to \nimport test scores by Student NAME\nRoster starts on line 3";
nameMessage+="\nName starts on column 1,\nhas 26 characters,\nthe start column for the score is 27\nthe length of the score column is 4";
String idMessage="The output below is in the format GQ uses to \nimport test scores by Student ID\nRoster starts on line 3";
idMessage+="\nID starts on column 1,\nhas 10 characters,\nthe start column for the score is 11\nthe length of the score column is 4";
JRadioButton name = new JRadioButton("<html><font color=black>Scores by NAME</font></html>");
JRadioButton id = new JRadioButton("<html><font color=black>Scores by ID</font></html>");
name.setSelected(true);
ButtonGroup group = new ButtonGroup();
group.add(name);
group.add(id);
Object[] array = {
new JLabel("GQ can import scores by NAME or by ID. Choose a method:"),
name,
id
};
int res = JOptionPane.showConfirmDialog(this, array, "Show GQ File",
JOptionPane.OK_CANCEL_OPTION);
if (res == JOptionPane.CANCEL_OPTION) return;
if (res==JOptionPane.CLOSED_OPTION) return;
if (name.isSelected())
res = JOptionPane.showConfirmDialog((Component) this, nameMessage,
"Import scores by NAME Info", JOptionPane.DEFAULT_OPTION);
if (id.isSelected())
res = JOptionPane.showConfirmDialog((Component) this, idMessage,
"Import scores by ID Info", JOptionPane.DEFAULT_OPTION);
setCurve();
for (int i=0; i< names.size();i++){
Student s = names.get(i).getStudent();
if (name.isSelected())
message.append("\r\n"+s.getGQName());
if (id.isSelected())
message.append("\r\n"+s.getGQID());
}
}
public void saveGQDialog(){
String nameMessage="The output below is in the format GQ uses to \nimport test scores by Student NAME\nRoster starts on line 3";
nameMessage+="\nName starts on column 1,\nhas 26 characters,\nthe start column for the score is 27\nthe length of the score column is 4";
String idMessage="The output below is in the format GQ uses to \nimport test scores by Student ID\nRoster starts on line 3";
idMessage+="\nID starts on column 1,\n has 10 characters,\nthe start column for the score is 11\nthe length of the score column is 4";
JRadioButton name = new JRadioButton("<html><font color=black>Scores by NAME</font></html>");
JRadioButton id = new JRadioButton("<html><font color=black>Scores by ID</font></html>");
name.setSelected(true);
ButtonGroup group = new ButtonGroup();
group.add(name);
group.add(id);
Object[] array = {
new JLabel("GQ can import scores by NAME or by ID. Choose a method:"),
name,
id
};
int res = JOptionPane.showConfirmDialog(this, array, "Show GQ File",
JOptionPane.OK_CANCEL_OPTION);
if (res == JOptionPane.CANCEL_OPTION) return;
if (res==JOptionPane.CLOSED_OPTION) return;
if (name.isSelected()){
res = JOptionPane.showConfirmDialog((Component) this, nameMessage,
"Import scores by NAME Info", JOptionPane.DEFAULT_OPTION);
}
if (id.isSelected()){
res = JOptionPane.showConfirmDialog((Component) this, idMessage,
"Import scores by ID Info", JOptionPane.DEFAULT_OPTION);
}
setCurve();
String path = System.getProperty("user.dir");;
JFileChooser chooser = new JFileChooser(path);
if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION)
{
try {
PrintWriter out = new PrintWriter(chooser.getSelectedFile());
String line=curveExplanation;
out.print(line);
for (int i=0; i< names.size();i++){
Student s = names.get(i).getStudent();
if (name.isSelected())
line="\r\n"+s.getGQName();
if (id.isSelected())
line="\r\n"+s.getGQID();
out.print(line);
}
out.print("\r\n");
out.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
BarChart.java
import java.awt.*;
import java.util.ArrayList;
@SuppressWarnings("serial")
public class BarChart extends Canvas
{
int[] raw;
int[]curve;
int scale=10;
ArrayList<LineItem> names;
Stats rStats,cStats;
String message1="";
String message2="";
public BarChart(ArrayList<LineItem> names)
{
super();
raw=new int[20];
curve=new int[20];
for(int i=0;i<20;i++){
raw[i]=0;
curve[i]=0;
}
this.names=names;
message1="";
message2="";
rStats=new Stats();
cStats=new Stats();
}
public void paint(Graphics g){
refresh();
Color c= new Color((int)(255*Math.random()),
(int)(255*Math.random()),
(int)(255*Math.random()));
g.setColor(c);
int x1=this.getWidth()/2;
for(int i=0; i<20;i++ ){
g.fillRect(x1-raw[i]*scale-10,8+20*i, raw[i]*scale, 10);//raw
g.fillRect(x1+25,8+20*i, curve[i]*scale, 10);//curve
}
//Dimension d= this.getSize();
g.setColor(Color.BLACK);
g.drawString(rStats.outliers(), 20, 20);
g.drawString("n="+rStats.getN(), 20, 40);
g.drawString("mean="+rStats.mean(), 20, 60);
g.drawString("min="+rStats.min(), 20, 80);
g.drawString("Q1="+rStats.q1(), 20, 100);
g.drawString("med="+rStats.med(), 20, 120);
g.drawString("Q3="+rStats.q3(), 20, 140);
g.drawString("max="+rStats.max(), 20, 160);
g.drawString("Before curve: "+rStats.gradeCount(), 20, getHeight()-22);
g.setColor(Color.blue);
g.drawString("After the curve:", 20, getHeight()-182);
g.drawString(cStats.outliers(), 20, getHeight()-162);
g.drawString("mean="+cStats.mean(), 20, getHeight()-142);
g.drawString("min="+cStats.min(), 20, getHeight()-122);
g.drawString("Q1="+cStats.q1(), 20, getHeight()-102);
g.drawString("med="+cStats.med(), 20, getHeight()-82);
g.drawString("Q3="+cStats.q3(), 20, getHeight()-62);
g.drawString("max="+cStats.max(), 20, getHeight()-42);
g.drawString("After curve: "+cStats.gradeCount(), 20, getHeight()-2);
//g.drawString("width = "+this.getWidth(), 20, 60);
g.setColor(Color.black);
for (int i=5;i<=100; i+=5){
g.drawString(""+i, x1, 5+4*i);
}
}
public void refresh(){
rStats=new Stats();
cStats=new Stats();
for(int i=0;i<20;i++){
raw[i]=0;
curve[i]=0;
}
for(LineItem line:names){
Student n=line.getStudent();
int r=n.getRaw();
if (r>0)
rStats.add(r);
if(r>99){
raw[19]++;
} else if (r>=0){
raw[r/5]++;
}
int c=n.getCurve();
if (c>0)
cStats.add(c);
if(c>99){
curve[19]++;
} else if (r>=0){
curve[c/5]++;
}
}
}
}
Student.java
public class Student implements Comparable<Student>
{
private String id,last, first;
private int raw, curve;
public Student(String[] a){
id=a[0].trim();
last=a[1].trim();
first=a[2].trim();
first=first.replaceAll("/comma/", ",");
last=last.replaceAll("/comma/", ",");
try {
setRaw(Integer.parseInt(a[3].trim()));
setCurve(getRaw());
//System.out.print(a[3]);
}catch (Exception e){
setRaw(-1);
setCurve(-1);
//System.out.print("'"+a[3]+"'");
}
}
public Student(int i, String n) {
id =""+i;
last = n.trim();
first="";
setRaw(-1);
setCurve(-1);
}
public String getName(){
String result= id+" "+first+" "+last;
return result;
}
public String getGQName(){
String result = last+", "+first;
while (result.length()<26)
result+=" ";
if (curve<0) {
result+="**";
}else{
result+=curveString();
}
return result;
}
public String getGQID(){
String result = id;
while (result.length()<10)
result+=" ";
if (curve<0) {
result+="**";
}else{
result+=curveString();
}
return result;
}
public String getLast(){
return last;
}
public String getFirst(){
return first;
}
public void setRaw(int raw) {
this.raw = raw;
}
public int getRaw() {
return raw;
}
public void setCurve(int score) {
this.curve = score;
}
public int getCurve() {
return curve;
}
public String curveString(){
String result=""+curve;
while (result.length()<3)
result=" "+result;
return result;
}
@Override
public int compareTo(Student o) {
String thisLast = last.toUpperCase();
String thisFirst = first.toUpperCase();
String otherLast = o.getLast().toUpperCase();
String otherFirst = o.getFirst().toUpperCase();
if (thisLast.equals(otherLast)){
return thisFirst.compareTo(otherFirst);
}
return thisLast.compareTo(otherLast);
}
}
Rational.java
public class Rational {
private int numerator;
private int denominator;
public Rational(String s){
int fBar=s.indexOf("/");
if (fBar==-1){
numerator=Integer.parseInt(s);
denominator=1;
} else {
numerator=Integer.parseInt(s.substring(0,fBar));
denominator=Integer.parseInt(s.substring(fBar+1));
simplify();
}
}
public Rational(int n, int d){
numerator=n;
denominator=d;
simplify();
}
public Rational(int n){
numerator=n;
denominator=1;
}
public Rational(){
numerator=0;
denominator=1;
}
public Rational (Rational r){
this.numerator=r.getNumerator();
this.denominator=r.getDenominator();
}
public void setNumerator(int numerator) {
this.numerator = numerator;
}
public int getNumerator() {
return numerator;
}
public void setDenominator(int denominator) {
this.denominator = denominator;
}
public int getDenominator() {
return denominator;
}
public double value(){
return (double)(numerator)/denominator;
}
public String toString(){
if (denominator ==1)
return Integer.toString(numerator);
return Integer.toString(numerator)+"/"+Integer.toString(denominator);
}
public Rational multiply(Rational other){
int n=this.getNumerator()*other.getNumerator();
int d=this.getDenominator()*other.getDenominator();
Rational result=new Rational(n,d);
return result;
}
public Rational add(Rational other){
int n=this.getNumerator()*other.getDenominator()+
other.getNumerator()*this.getDenominator();
int d=this.getDenominator()*other.getDenominator();
Rational result=new Rational(n,d);
return result;
}
public void simplify(){
int p=2;
while (p<= Math.max(numerator, denominator)){
while(numerator%p==0 && denominator%p==0){
numerator/=p;
denominator/=p;
}
if (p==2) p++; else p+=2;
}
if (denominator<0){
denominator*=-1;
numerator*=-1;
}
}
}
LineItem.java
import java.awt.Dimension;
import java.awt.Label;
import java.awt.TextField;
import java.awt.event.ActionListener;
import java.util.Comparator;
public class LineItem implements Comparable<LineItem>
{
private Student student;
private TextField rawScore;
private Label curveScore;
private Label nameLabel;
public final static int NamesAscending=1;
public final static int NamesDescending=2;
public final static int ScoresAsecending=3;
public final static int ScoresDescending=4;
public LineItem(Student s, ActionListener al){
setStudent(s);
nameLabel = new Label(s.getName());
nameLabel.setPreferredSize(new Dimension(270, 23));
rawScore = new TextField(3);
if (s.getRaw()>=0)
rawScore.setText(""+s.getRaw());
rawScore.addActionListener(al);
setCurveScore(new Label());
if (s.getCurve()>=0)
curveScore.setText(""+s.getCurve());
}
public void setStudent(Student name) {
this.student = name;
}
public Student getStudent() {
return student;
}
public void setCurveScore(Label curveScore) {
this.curveScore = curveScore;
}
public Label getCurveScore() {
return curveScore;
}
public TextField getRawScoreField() {
return rawScore;
}
public void updateCurveScore(){
if (student.getCurve()>=0)
curveScore.setText(""+student.getCurve());
}
public void updateRawScore(){
//make sure student records newly updated score in rawScore TextField
try{
student.setRaw(Integer.parseInt(rawScore.getText()));
}catch (NumberFormatException e){
student.setRaw(-1);
}
}
public void setNameLabel(Label nameLabel) {
this.nameLabel = nameLabel;
}
public Label getNameLabel() {
return nameLabel;
}
@Override
public int compareTo(LineItem other) {
return getStudent().compareTo(other.getStudent());
}
public static Comparator<LineItem> ScoreComparator = new Comparator<LineItem>(){
@Override
public int compare(LineItem o1, LineItem o2) {
int score1 = o1.getStudent().getRaw();
int score2 = o2.getStudent().getRaw();
return score1-score2;
}
};
}
Stats.java
import java.util.ArrayList;
import java.util.Collections;
public class Stats
{
private ArrayList<Integer> scores;
private int n;
private int sum;
public Stats(){
scores = new ArrayList<Integer>();
n=0;
sum=0;
}
public void add(int x){
scores.add(x);
sum+=x;
n++;
}
public void add(String s){
scores.add(Integer.parseInt(s));
}
public double mean() {
if (n==0) return 0;
return (double)(100*sum/n)/100.0;
}
public double med(){
if (n==0) return 0.0;
Collections.sort(scores);
if (n%2==0){
return (scores.get(n/2-1)+scores.get(n/2))/2.0;
}
return (scores.get(n/2));
}
public double q1(){
if (n==0) return 0.0;
Collections.sort(scores);
if (n%4==0){
return (scores.get(n/4-1)+scores.get(n/4))/2.0;
}
return (scores.get(n/4));
}
public double q3(){
if (n==0) return 0.0;
Collections.sort(scores);
if (n%4==0){
return (scores.get(3*n/4-1)+scores.get(3*n/4))/2.0;
}
return (scores.get(3*n/4));
}
public double min(){
if (n==0) return 0.0;
Collections.sort(scores);
return (scores.get(0));
}
public double max(){
if (n==0) return 0.0;
Collections.sort(scores);
return (scores.get(n-1));
}
public int getN() {
// TODO Auto-generated method stub
return n;
}
public String gradeCount(){
if (n==0) return "";
int[] g=new int[5];
for(int i =0; i<n; i++){
int s=scores.get(i);
if (s<60) g[0]++;
if (s>=60 && s<70) g[1]++;
if (s>=70 && s<80) g[2]++;
if (s>=80 && s<90) g[3]++;
if (s>=90 ) g[4]++;
}
return g[4]+" A's, "+g[3]+" B's, "+g[2]+" C's, "+g[1]+" D's, "+g[0]+" F's";
}
public String outliers(){
String result = "Outliers: ";
double iqr=q3()-q1();
double low=q1()-1.5*iqr;
double high=q3()+1.5*iqr;
for (int i=0; i<n; i++){
int s=scores.get(i);
if (s< low || s> high)
result+=s+" ";
}
if (result.length()==10) return "No outliers";
return result;
}
}
