/* Derby - Class org.apache.derbyDemo.scores.data.Data Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.apache.derbyDemo.scores.data; import java.sql.*; import org.apache.derbyDemo.scores.util.*; /** * <p> * Data used by Scores application * </p> * */ public class Data { //////////////////////////////////////////////////////// // // CONSTANTS // //////////////////////////////////////////////////////// private static final int QUESTIONS_PER_TEMPLATE = 10; private static final double IMPROVEMENT_PER_TAKING = 2.0; private static final double GRAMMAR_SCHOOL_PENALTY = 20.0; private static final double PER_STUDENT_FLUCTUATION = 3.0; private static final double FLUCTATION_MULTIPLIER = 2.0; public static final School LincolnGrammar = new School( "Lincoln Grammar", true ); public static final School WashingtonHS = new School( "Washington HS", false ); public static final Student AliceToklas = new Student ( "Alice", "Toklas", "1998-03-21", LincolnGrammar ); public static final Student GertrudeStein = new Student ( "Gertrude", "Stein", "1999-07-15", LincolnGrammar ); public static final Student HenryJames = new Student ( "Henry", "James", "2000-09-03", LincolnGrammar ); public static final Student GoreVidal = new Student ( "Gore", "Vidal", "2008-12-25", LincolnGrammar ); public static final Student TrumanCapote = new Student ( "Truman", "Capote", "1992-08-05", WashingtonHS ); public static final Student WaltWhitman = new Student ( "Walt", "Whitman", "1991-12-28", WashingtonHS ); public static final Student DorothyParker = new Student ( "Dorothy", "Parker", "1990-01-09", WashingtonHS ); public static final Student AndrewSullivan = new Student ( "Andrew", "Sullivan", "1989-02-20", WashingtonHS ); public static final Test GradeSchoolMath = new Test( "Grade School Math", true, 90.0 ); public static final Test GradeSchoolEnglish = new Test( "Grade School English", true, 85.0 ); public static final Test HighSchoolMath = new Test( "High School Math", false, 80.0 ); public static final Test HighSchoolEnglish = new Test( "High School English", false, 75.0 ); public static final Question GSM_1 = new Question( "GSM_1", 1, 5, 2, GradeSchoolMath ); public static final Question GSM_2 = new Question( "GSM_2", 2, 3, 1, GradeSchoolMath ); public static final Question GSM_3 = new Question( "GSM_3", 3, 4, 3, GradeSchoolMath ); public static final Question GSE_1 = new Question( "GSE_1", 3, 5, 2, GradeSchoolEnglish ); public static final Question GSE_2 = new Question( "GSE_2", 2, 3, 1, GradeSchoolEnglish ); public static final Question GSE_3 = new Question( "GSE_3", 1, 4, 3, GradeSchoolEnglish ); public static final Question HSM_1 = new Question( "HSM_1", 1, 5, 2, HighSchoolMath ); public static final Question HSM_2 = new Question( "HSM_2", 2, 3, 1, HighSchoolMath ); public static final Question HSM_3 = new Question( "HSM_3", 3, 4, 3, HighSchoolMath ); public static final Question HSE_1 = new Question( "HSE_1", 3, 5, 2, HighSchoolEnglish ); public static final Question HSE_2 = new Question( "HSE_2", 2, 3, 1, HighSchoolEnglish ); public static final Question HSE_3 = new Question( "HSE_3", 1, 4, 3, HighSchoolEnglish ); public static final Question[] QUESTIONS = { GSM_1, GSM_2, GSM_3, GSE_1, GSE_2, GSE_3, HSM_1, HSM_2, HSM_3, HSE_1, HSE_2, HSE_3, }; public static final School[] SCHOOLS = { LincolnGrammar, WashingtonHS, }; public static final Student[] STUDENTS = { AliceToklas, GertrudeStein, TrumanCapote, WaltWhitman, HenryJames, GoreVidal, DorothyParker, AndrewSullivan, }; public static final Test[] TESTS = { GradeSchoolMath, GradeSchoolEnglish, HighSchoolMath, HighSchoolEnglish, }; //////////////////////////////////////////////////////// // // STATE // //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// // // INNER CLASSES // //////////////////////////////////////////////////////// public abstract static class KeyedObject { private Integer _key; public void create( Database database ) throws SQLException { Logger log = Logger.getLogger(); boolean loggingEnabled = log.isLoggingEnabled(); // don't print out the chatter from creating these rows. try { log.enableLogging( false ); createMinion( database ); } finally { log.enableLogging( loggingEnabled ); } } protected abstract void createMinion( Database database ) throws SQLException; protected abstract PreparedStatement getKeyFinder ( Database database ) throws SQLException; public int getPrimaryKey( Database database ) throws SQLException { if ( _key == null ) { PreparedStatement ps = getKeyFinder( database ); setPrimaryKey( Utils.getScalarValue( ps ) ); } return _key.intValue(); } private void setPrimaryKey( int key ) { _key = new Integer( key ); } public SQLException notImplemented() { return new SQLException( "Not implemented." ); } } public static class School extends KeyedObject { private String _schoolName; private boolean _isGrammarSchool; public School( String schoolName, boolean isGrammarSchool ) { _schoolName = schoolName; _isGrammarSchool = isGrammarSchool; } public String getSchoolName() { return _schoolName; } public boolean isGrammarSchool() { return _isGrammarSchool; } protected void createMinion( Database database ) throws SQLException { Connection conn = database.getConnection(); PreparedStatement ps = Utils.prepare ( conn, "insert into School( schoolName ) values ( ? )" ); ps.setString( 1, _schoolName ); ps.execute(); Utils.close( ps ); } protected PreparedStatement getKeyFinder( Database database ) throws SQLException { Connection conn = database.getConnection(); PreparedStatement ps = Utils.prepare ( conn, "select schoolID from School where schoolName = ?" ); ps.setString( 1, _schoolName ); return ps; } } public static class Student extends KeyedObject { private String _firstName; private String _lastName; private String _birthday; private School _school; public Student ( String firstName, String lastName, String birthday, School school ) { _firstName = firstName; _lastName = lastName; _birthday = birthday; _school = school; } public String getFirstName() { return _firstName; } public String getLastName() { return _lastName; } public String getBirthday() { return _birthday; } public School getSchool() { return _school; } protected void createMinion( Database database ) throws SQLException { int param = 1; Connection conn = database.getConnection(); java.sql.Date sqlDate = java.sql.Date.valueOf( _birthday ); PreparedStatement ps = Utils.prepare ( conn, "insert into Student\n" + "( schoolID, lastName, firstName, birthday )\n" + "values( ?, ?, ?, ? )" ); ps.setInt( param++, _school.getPrimaryKey( database ) ); ps.setString( param++, _lastName ); ps.setString( param++, _firstName ); ps.setDate( param++, sqlDate ); ps.execute(); Utils.close( ps ); } protected PreparedStatement getKeyFinder( Database database ) throws SQLException { Connection conn = database.getConnection(); PreparedStatement ps = Utils.prepare ( conn, "select studentID from Student\n" + "where lastName = ? and firstName = ?" ); ps.setString( 1, _lastName ); ps.setString( 2, _firstName ); return ps; } } public static class Test extends KeyedObject { private String _testName; private boolean _grammarSchoolTest; private double _highSchoolScore; public Test ( String testName, boolean grammarSchoolTest, double highSchoolScore ) { _testName = testName; _grammarSchoolTest = grammarSchoolTest; _highSchoolScore = highSchoolScore; } public String getTestName() { return _testName; } public boolean isGrammarSchoolTest() { return _grammarSchoolTest; } public double getHighSchoolScore() { return _highSchoolScore; } protected void createMinion( Database database ) throws SQLException { Connection conn = database.getConnection(); PreparedStatement ps = Utils.prepare ( conn, "insert into Test( testName ) values ( ? )" ); ps.setString( 1, _testName ); ps.execute(); Utils.close( ps ); } protected PreparedStatement getKeyFinder( Database database ) throws SQLException { Connection conn = database.getConnection(); PreparedStatement ps = Utils.prepare ( conn, "select testID from Test where testName = ?" ); ps.setString( 1, _testName ); return ps; } } public static class TestTaking extends KeyedObject { private Student _student; private Test _test; public TestTaking( Student student, Test test ) { _student = student; _test = test; } public Student getStudent() { return _student; } public Test getTest() { return _test; } protected void createMinion( Database database ) throws SQLException { Connection conn = database.getConnection(); int param = 1; PreparedStatement ps = Utils.prepare ( conn, "insert into TestTaking\n" + "( studentID, testID, score ) values ( ?, ?, ? )" ); ps.setInt( param++, _student.getPrimaryKey( database ) ); ps.setInt( param++, _test.getPrimaryKey( database ) ); ps.setInt( param++, -1 ); ps.execute(); Utils.close( ps ); } protected PreparedStatement getKeyFinder( Database database ) throws SQLException { Connection conn = database.getConnection(); int param = 1; PreparedStatement ps = Utils.prepare ( conn, "select max( takingID ) from TestTaking\n" + "where studentID = ? and testID = ?" ); ps.setInt( param++, _student.getPrimaryKey( database ) ); ps.setInt( param++, _test.getPrimaryKey( database ) ); return ps; } } public static class Question extends KeyedObject { private String _questionName; private int _difficulty; private int _numberOfChoices; private int _correctChoice; private Test _test; public Question ( String questionName, int difficulty, int numberOfChoices, int correctChoice, Test test ) { _questionName = questionName; _difficulty = difficulty; _numberOfChoices = numberOfChoices; _correctChoice = correctChoice; _test = test; } public String getQuestionName() { return _questionName; } public int getDifficulty() { return _difficulty; } public int getNumberOfChoices() { return _numberOfChoices; } public int getCorrectChoice() { return _correctChoice; } public Test getTest() { return _test; } protected void createMinion( Database database ) throws SQLException { Connection conn = database.getConnection(); int param = 1; PreparedStatement ps = Utils.prepare ( conn, "insert into Question\n" + "( testID, questionName, difficulty, " + "numberOfChoices, correctChoice )\n" + "values ( ?, ?, ?, ?, ? )" ); ps.setInt( param++, _test.getPrimaryKey( database ) ); ps.setString( param++, _questionName ); ps.setInt( param++, _difficulty ); ps.setInt( param++, _numberOfChoices ); ps.setInt( param++, _correctChoice ); ps.execute(); Utils.close( ps ); } protected PreparedStatement getKeyFinder( Database database ) throws SQLException { Connection conn = database.getConnection(); PreparedStatement ps = Utils.prepare ( conn, "select questionID from Question where questionName = ?" ); ps.setString( 1, _questionName ); return ps; } } public static class QuestionTaking extends KeyedObject { private TestTaking _testTaking; private Question _question; private int _actualChoice; public QuestionTaking ( TestTaking testTaking, Question question, int actualChoice ) { _testTaking = testTaking; _question = question; _actualChoice = actualChoice; } public TestTaking getTestTaking() { return _testTaking; } public Question getQuestion() { return _question; } public int getActualChoice() { return _actualChoice; } protected void createMinion( Database database ) throws SQLException { Connection conn = database.getConnection(); int param = 1; PreparedStatement ps = Utils.prepare ( conn, "insert into QuestionTaking\n" + "( questionID, takingID, actualChoice ) values ( ?, ?, ? )" ); ps.setInt( param++, _question.getPrimaryKey( database ) ); ps.setInt( param++, _testTaking.getPrimaryKey( database ) ); ps.setInt( param++, _actualChoice ); ps.execute(); Utils.close( ps ); } protected PreparedStatement getKeyFinder( Database database ) throws SQLException { // no primary key for this table! throw notImplemented(); } } //////////////////////////////////////////////////////// // // CONSTRUCTOR // //////////////////////////////////////////////////////// /** * <p> * Create an object to populate the tables for the Scores application. * </p> */ public Data() { } //////////////////////////////////////////////////////// // // PUBLIC BEHAVIOR // //////////////////////////////////////////////////////// /** * <p> * Initialize the Scores application with data. * </p> */ public void initialize( Database database ) throws SQLException { initialize( database, SCHOOLS ); initialize( database, STUDENTS ); initialize( database, TESTS ); initializeQuestions( database ); } /** * <p> * Make the students take their tests. * </p> */ public void takeTests( Database database ) throws SQLException { int testCount = TESTS.length; int takings = 0; for ( int i = 0; i < testCount; i++ ) { takings = takeTest( database, TESTS[ i ], takings ); } } /** * <p> * Make all eligible students take the test. * Grammar school tests are taken * by all students. High school tests are only taken by high school * students. Returns the number of test takings so far (this is used * to throttle diagnostic noise. * </p> */ public int takeTest( Database database, Test test, int takings ) throws SQLException { Logger log = Logger.getLogger(); boolean loggingEnabled = log.isLoggingEnabled(); int studentCount = STUDENTS.length; double highSchoolScore = test.getHighSchoolScore(); double grammarSchoolScore = highSchoolScore - GRAMMAR_SCHOOL_PENALTY; double fluctuation; double targetScore; for ( int i = 0; i < studentCount; i++ ) { Student student = STUDENTS[ i ]; boolean grammarSchoolStudent = student.getSchool().isGrammarSchool(); if ( grammarSchoolStudent && (!test.isGrammarSchoolTest()) ) { continue; } if ( grammarSchoolStudent ) { targetScore = grammarSchoolScore; } else { targetScore = highSchoolScore; } fluctuation = (((double) i) * FLUCTATION_MULTIPLIER) * (-1.0); targetScore += fluctuation; try { // // Only print out the first test taking. // They all look alike. // if ( takings > 0 ) { log.enableLogging( false ); } takeTest( database, student, test, targetScore ); takings++; } finally { log.enableLogging( loggingEnabled ); } } return takings; } /** * <p> * Take a test. * </p> */ public void takeTest ( Database database, Student student, Test test, double initialTargetScore ) throws SQLException { Logger log = Logger.getLogger(); boolean loggingEnabled = log.isLoggingEnabled(); TestTaking testTaking; // don't print out the chatter from creating these rows. try { log.enableLogging( false ); testTaking = takeTestingMinion ( database, student, test, initialTargetScore ); } finally { log.enableLogging( loggingEnabled ); } // I want to see this, though finishTest( database, testTaking ); } /** * <p> * The guts of test taking, removed here * to a separate method so that we can turn off logging. * </p> */ private TestTaking takeTestingMinion ( Database database, Student student, Test test, double initialTargetScore ) throws SQLException { TestTaking testTaking = new TestTaking( student, test ); Connection conn = database.getConnection(); double targetScore = makeTargetScore ( database, student, test, initialTargetScore ); // // Initiate the test. // testTaking.create( database ); // // Now get the questions which have to be answered. // PreparedStatement ps = Utils.prepare ( conn, "select questionName, difficulty, numberOfChoices, " + "correctChoice from Question\n" + "where testID = ?" ); ps.setInt( 1, test.getPrimaryKey( database ) ); ResultSet rs = ps.executeQuery(); double weightSoFar = 0.0; double answersSoFar = 0.0; double scoreSoFar = 0.0; while( rs.next() ) { int column = 1; Question question = new Question ( rs.getString( column++ ), rs.getInt( column++ ), rs.getInt( column++ ), rs.getInt( column++ ), test ); int correctChoice = question.getCorrectChoice(); int difficulty = question.getDifficulty(); int actualChoice; // // Answer the question correctly if we haven't achieved our // target score. // if ( scoreSoFar <= targetScore ) { actualChoice = correctChoice; } else { actualChoice = correctChoice - 1; } weightSoFar += Utils.weighQuestion( difficulty ); answersSoFar += Utils.scoreAnswer ( difficulty, question.getNumberOfChoices(), correctChoice, actualChoice ); scoreSoFar = Utils.finishScore( weightSoFar, answersSoFar ); QuestionTaking questionTaking = new QuestionTaking( testTaking, question, actualChoice ); questionTaking.create( database ); } return testTaking; } /** * <p> * The student's grade should improve with each taking. * </p> */ private double makeTargetScore ( Database database, Student student, Test test, double initialTargetScore ) throws SQLException { int param = 1; PreparedStatement ps = Utils.prepare ( database.getConnection(), "select count(*) from TestTaking\n" + "where studentID = ? and testID = ?" ); ps.setInt( param++, student.getPrimaryKey( database ) ); ps.setInt( param++, test.getPrimaryKey( database ) ); int takingCount = Utils.getScalarValue( ps ); return (((double) takingCount) * IMPROVEMENT_PER_TAKING) + initialTargetScore; } /** * <p> * Finish taking a Test. This involves updating the datestamp and * triggering Test scoring. * </p> */ private void finishTest( Database database, TestTaking testTaking ) throws SQLException { Connection conn = database.getConnection(); int param = 1; java.sql.Date date = new java.sql.Date( System.currentTimeMillis() ); PreparedStatement ps = Utils.prepare ( conn, "update TestTaking set takingDate = ? where takingID = ?\n" ); ps.setDate( param++, date ); ps.setInt( param++, testTaking.getPrimaryKey( database ) ); ps.execute(); Utils.close( ps ); } /** * <p> * Report which students in the given school incorrectly answered the * given question. * </p> */ public void reportWhoNeedsImprovement ( Database database, School school, String questionName ) throws SQLException { Connection conn = database.getConnection(); int param = 1; PreparedStatement ps = Utils.prepare ( conn, "select st.lastName, st.firstName\n" + "from School sc, Student st,\n" + " LastTaking lt, QuestionTaking qt,\n" + " Question q\n" + "where\n" + "q.questionName = ?\n" + "and sc.schoolID = ?\n" + "\n" + "and q.questionID = qt.questionID\n" + "and sc.schoolID = st.schoolID\n" + "\n" + "and lt.testID = q.testID\n" + "and lt.studentID = st.studentID\n" + "and lt.takingID = qt.takingID\n" + "\n" + "and scoreAnswer"+ "( q.difficulty, q.numberOfChoices, " + "q.correctChoice, qt.actualChoice )\n" + " < weighQuestion( q.difficulty )\n" + "\n" + "order by st.lastName, st.firstName\n" ); ps.setString( param++, questionName ); ps.setInt( param++, school.getPrimaryKey( database ) ); ResultSet rs = ps.executeQuery(); Database.prettyPrint( conn, rs ); Utils.close( rs ); Utils.close( ps ); } //////////////////////////////////////////////////////// // // MINIONS // //////////////////////////////////////////////////////// /** * <p> * Create a bunch of questions from the template questions. * </p> */ private void initializeQuestions( Database database ) throws SQLException { int count = QUESTIONS.length; for ( int i = 0; i < count; i++ ) { Question template = QUESTIONS[ i ]; for ( int j = 0; j < QUESTIONS_PER_TEMPLATE; j++ ) { Question clone = new Question ( template.getQuestionName() + '_' + j, template.getDifficulty(), template.getNumberOfChoices(), template.getCorrectChoice(), template.getTest() ); clone.create( database ); } } } /** * <p> * Populate a table with its objects. * </p> */ private void initialize( Database database, KeyedObject[] objects ) throws SQLException { int count = objects.length; for ( int i = 0; i < count; i++ ) { objects[ i ].create( database ); } } }