001 /* PAVLOV -- Multiple Choice Study System
002 * Copyright (C) 2000 - 2004 T.J. Willis
003 *
004 * This program is free software; you can redistribute it and/or
005 * modify it under the terms of the GNU General Public License
006 * as published by the Free Software Foundation; either version 2
007 * of the License, or (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program; if not, write to the Free Software
016 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
017 *
018 * $Header: /cvsroot/pavlov/net/sourceforge/pavlov/user/ChapterData.java,v 1.9 2004/07/01 09:03:44 tj_willis Exp $
019 */
020 package net.sourceforge.pavlov.user;
021
022 import java.util.*;
023
024 import net.sourceforge.pavlov.library.AbstractChapter;
025 import net.sourceforge.pavlov.library.Question;
026 import org.apache.log4j.*;
027
028
029 /**
030 * Encapsulates a users responses to all questions in a chapter.
031 *
032 * @author <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a>
033 * @version $Revision: 1.9 $
034 * @see net.sourceforge.pavlov.library.Chapter
035 * @see BookData
036 * @see QuestionData
037 */
038 public final class ChapterData {
039 // Unit coverage in TestQuiz.java
040 /** Title of the corresponding Chapter */
041 private String title;
042 /** Holds QuestionData. */
043 private Hashtable<String,QuestionData> questions;
044 /** Strategy to use in choosing next question. */
045 private AbstractStrategy strategy = null;
046 /** Detailed statistics about responses to this chapter. */
047 private ChapterStatistics chapterStatistics;
048 /** Results of past quizzes. */
049 private QuizCollection quizCollection;
050 /** QuestionData's we don't want to ask*/
051 protected Vector<QuestionData> exclusion;
052 /** QuestionData's we don't want to ask*/
053 protected int exclusionSize = 5;
054
055 /**
056 * Creates a ChapterData with the given title
057 * @param _title a <code>String</code> value
058 */
059 public ChapterData(String title) {
060 this.title = title;
061 init();
062 }
063
064 private void init()
065 {
066 questions = new Hashtable<String,QuestionData>(150);
067 quizCollection = new QuizCollection();
068 exclusion = new Vector<QuestionData>();
069 }
070
071
072 // FIXME: title should be "name" to agree with library.Chapter
073
074 /** Creates a ChapterData with title "Untitled" */
075 public ChapterData() {
076 this("Untitled");
077 }
078
079 /**
080 * Sets the title of the Chapter this ChapterData refers to.
081 *
082 * @param t a <code>String</code> value
083 */
084 public void setTitle(String title) {
085 this.title = title;
086 }
087
088
089 /**
090 * Returns the title of the Chapter this ChapterData refers to.
091 *
092 * @return a <code>String</code> value
093 */
094 public String getTitle() {
095 return title;
096 }
097
098 /**
099 * Returns the QuestionData for the question with the given ID
100 * or null if no such question exists.
101 *
102 * @param qid a <code>String</code> value
103 * @return a <code>QuestionData</code> value
104 */
105 public QuestionData getQuestionData(final String qid) {
106 return questions.get(qid);
107 }
108
109 /**
110 * Adds a correct response to the named QuestionData at the
111 * current time.
112 *
113 * @param id a <code>String</code> value
114 */
115 public void addRight(final String id) {
116 Date now = new Date();
117 addRight(id, now);
118 }
119
120 /**
121 * Adds an incorrect response to the named QuestionData at the
122 * current time.
123 *
124 * @param id a <code>String</code> value
125 */
126 public void addWrong(final String id) {
127 Date now = new Date();
128 addWrong(id, now);
129 }
130
131 /**
132 * Returns the number of questions in this chapter.
133 *
134 * @return an <code>int</code> value
135 */
136 public int getNumberOfQuestions() {
137 if (questions == null) return 0;
138 return questions.size();
139 }
140
141 /**
142 * Adds a correct response to the named QuestionData at the
143 * given time.
144 *
145 * @param id a <code>String</code> value
146 * @param whe a <code>long</code> value
147 */
148 public void addRight(final String id, final long when) {
149 Date now = new Date(when);
150 addRight(id, now);
151 }
152
153 /**
154 * Adds an incorrect response to the named QuestionData at the
155 * given time.
156 *
157 * @param id a <code>String</code> value
158 * @param whe a <code>long</code> value
159 */
160 public void addWrong(final String id, final long whe) {
161 Date now = new Date(whe);
162 addWrong(id, now);
163 }
164
165 /**
166 * Adds a correct response to the named QuestionData at the
167 * given time.
168 *
169 * @param id a <code>String</code> value
170 * @param when a <code>Date</code> value
171 */
172 public void addRight(final String id, final Date when) {
173 QuestionData q = (QuestionData) questions.get(id);
174 if (q == null) {
175 q = new QuestionData(id);
176 questions.put(id, q);
177 }
178 q.addRight(when);
179 }
180
181 /**
182 * Adds an incorrect response to the named QuestionData at the
183 * given time.
184 *
185 * @param id a <code>String</code> value
186 * @param when a <code>Date</code> value
187 */
188 public void addWrong(final String id, final Date when) {
189 QuestionData q = (QuestionData) questions.get(id);
190 if (q == null) {
191 q = new QuestionData(id);
192 questions.put(id, q);
193 }
194 q.addWrong(when);
195 }
196
197 /**
198 * Loop through all the questions in the chapter, ensuring a
199 * QuestionData exists for each. If not, create a new QuestionData.
200 *
201 * @param cp an <code>AbstractChapter</code> value
202 */
203 public void validate(AbstractChapter cp) {
204 // foreach question in chapter
205 // get QuestionData for question
206 // if NEXIST, make a new one
207 Collection<Question> v = cp.getQuestionsReadOnly();
208
209 for(Question left: v) {
210 QuestionData da = (QuestionData) questions.get(left.getID());
211
212 if (da == null) {
213 QuestionData x = new QuestionData(left.getID());
214
215 questions.put(x.getID(), x);
216 }
217 // DON'T NEED x.validate(left) -- these are leaves
218 }
219 }
220
221 /**
222 * Returns the size of the exclusion vector.
223 *
224 * @return an <code>int</code> value
225 */
226 public int getExclusionSize()
227 {
228 return exclusionSize;
229 }
230
231 /**
232 * Sets the size of the exclusion vector.
233 *
234 * @param i an <code>int</code> value
235 */
236 public void setExclusionSize(final int newSize)
237 {
238 int x = newSize;
239 if(x<0){
240 x = 0;
241 }
242 exclusionSize = x;
243 }
244
245 /**
246 * Returns a QuestionData using the current strategy. If no strategy
247 * is current, create a BasicStrategy and use it.
248 *
249 * @return a <code>QuestionData</code> value
250 */
251 public QuestionData getQuestion() {
252 //assert strategy != null : "Null strategy in getQuestion";
253 if(strategy==null){
254 strategy = new BasicStrategy(questions.values());
255 }
256 QuestionData da = strategy.getQuestion(exclusion);
257 //FIXME: add this:
258 //while exclusion.size()>= # avail questions exclusion.remove(0);
259 if(exclusion.size()>exclusionSize){
260 exclusion.remove(0);
261 }
262 exclusion.add(da);
263 return da;
264 }
265
266 /**
267 * Returns the current question selection strategy.
268 *
269 * @return an <code>AbstractStrategy</code> value
270 */
271 public final AbstractStrategy getStrategy()
272 {
273 return strategy;
274 }
275
276
277 /**
278 * Sets the question selection strategy and initializes it with
279 * the QuestionData for this chapter.
280 *
281 * @param strat an <code>AbstractStrategy</code> value
282 */
283 public final void setStrategy(AbstractStrategy strat) {
284 if(strat==null) return;
285 strat.setQuestions(questions.values());
286 strategy = strat;
287 }
288
289 /**
290 * Returns the <code>ChapterStatistics</code> for
291 * this ChapterData.
292 *
293 * @return a <code>ChapterStatistics</code> value
294 */
295 public ChapterStatistics getChapterStatistics() {
296 if (chapterStatistics != null){
297 return chapterStatistics;
298 }
299 Collection<QuestionData> v = questions.values();
300 chapterStatistics = new ChapterStatistics();
301 for(QuestionData q: v){
302 chapterStatistics.addQuestionData(q);
303 }
304
305 return chapterStatistics;
306 }
307
308 /**
309 * Removes the QuestionData corresponding to the given ID.
310 *
311 * @param key a <code>String</code> value
312 */
313 public void deleteQuestionID(final String key) {
314 questions.remove(key);
315 }
316
317 /**
318 * Converts this ChapterData to XML.
319 *
320 * @param writer a <code>java.io.Writer</code> value
321 * @exception java.io.IOException if an error occurs
322 */
323 public void toXML(java.io.Writer writer)
324 throws java.io.IOException {
325 Collection<QuestionData> v = questions.values();
326
327 writer.write("\t\t<CHAPTER TITLE=\"" + title + "\">\n"); // LAST=\"19/Feb/02\">\n");
328
329 //x += "This chapter has " + v.size() + " questions w/ history\n";
330 for( QuestionData q : v ) {
331 q.toXML(writer);
332 }
333
334 quizCollection.toXML(writer);
335
336 writer.write("\t\t</CHAPTER>\n");
337
338 }
339
340 /**
341 * Adds the given QuizData object to this ChapterData's QuizCollection.
342 *
343 * @param quizData a <code>QuizData</code> value
344 */
345 public void addQuizData(final QuizData quizData) {
346 quizCollection.addQuiz(quizData);
347 }
348
349 /**
350 * Describe <code>getStatistics</code> method here.
351 *
352 * @deprecated Misleading name. Use getQuizCollectionData()
353 * @return a <code>QuizCollectionData</code> value
354 */
355 @Deprecated public QuizCollectionData getStatistics() {
356 return getQuizCollectionData();
357 }
358
359
360 /**
361 * Returns data for all quizzes the user has taken on this chapter.
362 *
363 * @return a <code>QuizCollectionData</code> value
364 */
365 public QuizCollectionData getQuizCollectionData() {
366 return quizCollection.getStatistics();
367 }
368
369 /**
370 * Returns a vector of percentages for all the quizzes the user
371 * has taken on this chapter.
372 *
373 * @return a <code>Vector</code> value
374 */
375 public Vector getQuizPercentages() {
376 return quizCollection.getPercentages();
377 }
378
379 /**
380 * Describe <code>getQuizCollection</code> method here.
381 *
382 * @return a <code>QuizCollection</code> value
383 */
384 public QuizCollection getQuizCollection()
385 {
386 return quizCollection;
387 }
388
389
390 // FIXME: make an unmodifiable list version of this method
391 /**
392 * Describe <code>getAllQuestionData</code> method here.
393 *
394 * @return a <code>Collection</code> value
395 */
396 public Collection getAllQuestionData()
397 {
398 return questions.values();
399 }
400 }
401
402
403
404
405
406
407
408