TermScheduler.cpp
Go to the documentation of this file.00001 #include "TermScheduler.h"
00002
00003 TermScheduler::TermScheduler( const Preferences& prefs ) : prefs( prefs ) {
00004 srand( time( NULL ) );
00005 }
00006
00007 TermScheduler::TermScheduler( const TermScheduler& scheduler )
00008 : quizFirstLang( scheduler.quizFirstLang ), quizTestLang( scheduler.quizTestLang ),
00009 standbyPool( scheduler.standbyPool ), allTerms( scheduler.allTerms ),
00010 initQuizLength( scheduler.initQuizLength ), initTermCount( scheduler.initTermCount ),
00011 currTermPool( scheduler.currTermPool ), currTerm( scheduler.currTerm ), prefs( scheduler.prefs ) {
00012 for( uint i = 0; i < poolCount; i++ )
00013 termPool[ i ] = scheduler.termPool[ i ];
00014 }
00015
00016 TermScheduler::~TermScheduler() {
00017 }
00018
00019 bool TermScheduler::isResumableQuizAvailable( const BilingualKey& key ) const {
00020 QFile dataFile( getFilename( key ) );
00021 return( dataFile.exists() );
00022 }
00023
00024 bool TermScheduler::load( const BilingualKey& key ) {
00025 QFile dataFile( getFilename( key ) );
00026 if( !dataFile.open( IO_ReadOnly ) )
00027 return( false );
00028
00029 QByteArray compressedData( dataFile.readAll() );
00030 QByteArray data( Util::qUncompress( compressedData ) );
00031
00032 QDataStream in( data, IO_ReadOnly );
00033
00034 Q_UINT32 tempMagicNumber;
00035 Q_UINT16 tempVersion;
00036
00037 in >> tempMagicNumber >> tempVersion;
00038
00039 if( tempMagicNumber != magicNumber ) {
00040 cerr << "Wrong magic number: Incompatible data file." << endl;
00041 return( false );
00042 }
00043 if( tempVersion > 0x0009 ) {
00044 cerr << "Data file is from a more recent version. Upgrade toMOTko." << endl;
00045 return( false );
00046 }
00047
00048 in.setVersion( 3 );
00049
00050 TermScheduler tempScheduler( prefs );
00051 in >> tempScheduler;
00052
00053 quizFirstLang = tempScheduler.quizFirstLang;
00054 quizTestLang = tempScheduler.quizTestLang;
00055 for( uint i = 0; i < poolCount; i++ )
00056 termPool[ i ] = tempScheduler.termPool[ i ];
00057 standbyPool = tempScheduler.standbyPool;
00058 allTerms = tempScheduler.allTerms;
00059 initQuizLength = tempScheduler.initQuizLength;
00060 initTermCount = tempScheduler.initTermCount;
00061 currTermPool = tempScheduler.currTermPool;
00062 currTerm = tempScheduler.currTerm;
00063
00064 return( true );
00065 }
00066
00067 bool TermScheduler::save() {
00068 QByteArray data;
00069
00070 QDataStream out( data, IO_WriteOnly );
00071 out.setVersion( 3 );
00072
00073
00074 out << Q_UINT32( TermScheduler::magicNumber ) << Q_UINT16( 0x0009 ) << *this;
00075
00076 QByteArray compressedData( Util::qCompress( data ) );
00077
00078 QFile dataFile( getFilename( BilingualKey( quizFirstLang, quizTestLang ) ) );
00079 QFileInfo dataFileInfo( dataFile );
00080
00081 if( !Util::makeDirectory( dataFileInfo.dirPath() ) )
00082 return( false );
00083
00084 if( !dataFile.open( IO_WriteOnly ) )
00085 return( false );
00086
00087 int ret = dataFile.writeBlock( compressedData );
00088 dataFile.close();
00089
00090 if( ret == -1 || dataFile.status() != IO_Ok ) {
00091 dataFile.resetStatus();
00092 return( false );
00093 }
00094
00095 return( true );
00096 }
00097
00098 void TermScheduler::concludeQuiz() {
00099 if( isQuizInProgress() )
00100 save();
00101 else {
00102 QString filename( getFilename( BilingualKey( quizFirstLang, quizTestLang ) ) );
00103 QFile dataFile( filename );
00104 if( dataFile.exists() ) {
00105 if( !dataFile.remove() )
00106 cerr << "Could not remove file " << filename << ". Status=" << dataFile.status() << endl;
00107 }
00108 }
00109 }
00110
00111 void TermScheduler::setApplicationDirName( const QString& applDir ) {
00112 this->applDir = applDir;
00113 }
00114
00115 QString TermScheduler::getQuizFirstLanguage() const {
00116 return( quizFirstLang );
00117 }
00118
00119 QString TermScheduler::getQuizTestLanguage() const {
00120 return( quizTestLang );
00121 }
00122
00123 bool TermScheduler::isQuizInProgress() const {
00124 for( uint i = 0; i < poolCount; i++ ) {
00125 if( termPool[ i ].count() > 0 )
00126 return( true );
00127 }
00128 return( standbyPool.count() > 0 );
00129 }
00130
00131 void TermScheduler::init( const QString& quizFirstLang, const QString& quizTestLang, Folder* rootFolder ) {
00132 this->quizFirstLang = quizFirstLang;
00133 this->quizTestLang = quizTestLang;
00134 initTermCount = 0;
00135 initQuizLength = prefs.getQuizLength();
00136 for( uint i = 0; i < poolCount; i++ )
00137 termPool[ i ].clear();
00138 allTerms.clear();
00139 initRec( quizFirstLang, quizTestLang, rootFolder );
00140 standbyPool.clear();
00141 currTermPool = 0;
00142 }
00143
00144 void TermScheduler::reinit() {
00145 for( uint i = 0; i < poolCount; i++ )
00146 termPool[ i ].clear();
00147 standbyPool.clear();
00148 for( QValueList<TermKey>::ConstIterator it = allTerms.begin(); it != allTerms.end(); it++ ) {
00149 TermKey termKey = *it;
00150 addTerm( termKey, ( poolCount - 1 ) - initQuizLength );
00151 }
00152 currTermPool = 0;
00153 }
00154
00155 void TermScheduler::initRec( const QString& quizFirstLang, const QString& quizTestLang, Folder* folder ) {
00156 if( folder->isMarkedForStudy() ) {
00157 for( Base* child = folder->first(); child; child = folder->next() ) {
00158 if( strcmp( child->className(), "Folder" ) == 0 )
00159 initRec( quizFirstLang, quizTestLang, (Folder*)child );
00160 else if( strcmp( child->className(), "Vocabulary" ) == 0 )
00161 initRec( quizFirstLang, quizTestLang, (Vocabulary*)child );
00162 }
00163 }
00164 }
00165
00166 void TermScheduler::initRec( const QString& quizFirstLang, const QString& quizTestLang, Vocabulary* vocab ) {
00167 if( vocab->isMarkedForStudy() ) {
00168 for( Vocabulary::TermMap::ConstIterator it = vocab->begin(); it != vocab->end(); it++ ) {
00169 const Term& term = it.data();
00170 if( term.isMarkedForStudy() &&
00171 term.isTranslationExists( quizFirstLang ) && term.isTranslationExists( quizTestLang ) ) {
00172 TermKey termKey( term.getId(), term.getVocabId() );
00173 addTerm( termKey, ( poolCount - 1 ) - initQuizLength );
00174 allTerms.append( termKey );
00175 initTermCount++;
00176 }
00177 }
00178 }
00179 }
00180
00181 void TermScheduler::addTerm( const TermKey& termKey, const int priority = 0 ) {
00182 termPool[ priority ].append( termKey );
00183 }
00184
00185 void TermScheduler::discardCurrentTerm() {
00186 termPool[ currTermPool ].remove( currTerm );
00187 }
00188
00189 const TermKey TermScheduler::getCurrentTerm() const {
00190 return( currTerm );
00191 }
00192
00193 bool TermScheduler::hasNextTerm() const {
00194 if( !standbyPool.isEmpty() )
00195 return( true );
00196
00197 for( uint i = 0; i < poolCount; i++ ) {
00198 if( !termPool[ i ].isEmpty() )
00199 return( true );
00200 }
00201
00202 return( false );
00203 }
00204
00205 const TermKey TermScheduler::getNextTerm() {
00206 if( standbyPool.count() == TermScheduler::standbyPoolSize )
00207 reintroduceStandbyTerm();
00208
00209 search:
00210 int startPool = ( currTermPool + 1 ) % TermScheduler::poolCount;
00211 int nextPool = startPool;
00212 do {
00213 const int poolSize = termPool[ nextPool ].count();
00214 if( poolSize > 0 ) {
00215 const int index = rand() % poolSize;
00216 currTermPool = nextPool;
00217 currTerm = termPool[ nextPool ][ index ];
00218 return( currTerm );
00219 }
00220 nextPool = ( nextPool + 1 ) % TermScheduler::poolCount;
00221 } while( nextPool != startPool );
00222 if( standbyPool.count() > 0 ) {
00223 reintroduceStandbyTerm();
00224 goto search;
00225 }
00226 else {
00227 currTerm = TermKey();
00228 currTermPool = -1;
00229 return( currTerm );
00230 }
00231 }
00232
00233 void TermScheduler::increaseTermPriority() {
00234 if( currTermPool > 0 ) {
00235 termPool[ currTermPool ].remove( currTerm );
00236 currTermPool--;
00237 termPool[ currTermPool ].append( currTerm );
00238 }
00239 }
00240
00241 void TermScheduler::decreaseTermPriority() {
00242 if( currTermPool < (int)( TermScheduler::poolCount - 1 ) ) {
00243 termPool[ currTermPool ].remove( currTerm );
00244 currTermPool++;
00245 termPool[ currTermPool ].append( currTerm );
00246 }
00247 }
00248
00249 void TermScheduler::rightAnswer() {
00250 if( currTermPool == TermScheduler::poolCount - 1 )
00251 discardCurrentTerm();
00252 else {
00253 decreaseTermPriority();
00254 putCurrentTermOnStandby();
00255 }
00256 }
00257
00258 void TermScheduler::wrongAnswer() {
00259 increaseTermPriority();
00260 putCurrentTermOnStandby();
00261 }
00262
00263 int TermScheduler::getProgress() const {
00264 if( initTermCount == 0 )
00265 return( 0 );
00266 int currTermCount = 0;
00267 int progress = 0;
00268 int parts = ( initQuizLength + 1 ) * initTermCount;
00269 for( uint i = 0; i < poolCount; i++ ) {
00270 currTermCount += termPool[ i ].count();
00271 if( i >= ( poolCount - initQuizLength ) ) {
00272 int incr = ( termPool[ i ].count() * ( initQuizLength + 1 - poolCount + i ) );
00273 progress += incr;
00274 }
00275 }
00276 for( uint i = 0; i < standbyPool.count(); i++ ) {
00277 currTermCount += 1;
00278 if( standbyPool[ i ].getPool() >= ( poolCount - initQuizLength ) ) {
00279 int incr = ( initQuizLength + 1 - poolCount + standbyPool[ i ].getPool() );
00280 progress += incr;
00281 }
00282 }
00283 progress += ( initTermCount - currTermCount ) * ( parts / initTermCount );
00284 int result = (int)( progress * 100 / parts );
00285 return( result );
00286 }
00287
00288 int TermScheduler::getInitialTermCount() const {
00289 return( initTermCount );
00290 }
00291
00292 void TermScheduler::putCurrentTermOnStandby() {
00293 StandbyTerm term( currTerm, currTermPool );
00294 standbyPool.prepend( term );
00295 termPool[ currTermPool ].remove( currTerm );
00296 }
00297
00298 void TermScheduler::reintroduceStandbyTerm() {
00299 const StandbyTerm& reintroducedTerm = standbyPool.last();
00300 termPool[ reintroducedTerm.getPool() ].append( reintroducedTerm.getKey() );
00301 standbyPool.remove( reintroducedTerm );
00302 }
00303
00304 QString TermScheduler::getFilename( const BilingualKey& key ) const {
00305 return( applDir + QString( "/quiz_" ) + key.toString() + QString( ".dat.z" ) );
00306 }
00307
00308 QDataStream& operator<<( QDataStream& out, const TermScheduler& scheduler ) {
00309 out << scheduler.quizFirstLang << scheduler.quizTestLang;
00310
00311 for( uint i = 0; i < TermScheduler::poolCount; i++ ) {
00312 for( QValueList<TermKey>::ConstIterator it = scheduler.termPool[ i ].begin(); it != scheduler.termPool[ i ].end(); it++ )
00313 out << *it;
00314 out << TermKey( 0, 0 );
00315 }
00316
00317 for( QValueList<StandbyTerm>::ConstIterator it = scheduler.standbyPool.begin(); it != scheduler.standbyPool.end(); it++ )
00318 out << *it;
00319 out << StandbyTerm( TermKey( 0, 0 ), 0 );
00320
00321 out << scheduler.allTerms;
00322 out << scheduler.initQuizLength << scheduler.initTermCount << scheduler.currTermPool << scheduler.currTerm;
00323
00324 return( out );
00325 }
00326
00327 QDataStream& operator>>( QDataStream& in, TermScheduler& scheduler ) {
00328 QString tempFirstLanguage;
00329 QString tempTestLanguage;
00330 QValueList<TermKey> tempTermPool[ TermScheduler::poolCount ];
00331 QValueList<StandbyTerm> tempStandbyPool;
00332 QValueList<TermKey> tempAllTerms;
00333 uint tempInitQuizLength;
00334 int tempInitTermCount;
00335 int tempCurrTermPool;
00336 TermKey tempCurrTerm;
00337
00338 in >> tempFirstLanguage >> tempTestLanguage;
00339 for( uint i = 0; i < TermScheduler::poolCount; i++ ) {
00340 for( ;; ) {
00341 TermKey tempTermKey;
00342 in >> tempTermKey;
00343 if( tempTermKey.getTermId() == 0 && tempTermKey.getVocabId() == 0 )
00344 break;
00345 tempTermPool[ i ].append( tempTermKey );
00346 }
00347 }
00348
00349 for( ;; ) {
00350 StandbyTerm tempStandbyTerm;
00351 in >> tempStandbyTerm;
00352 if( tempStandbyTerm.getPool() == 0 && tempStandbyTerm.getKey().getTermId() == 0 && tempStandbyTerm.getKey().getVocabId() == 0 )
00353 break;
00354 tempStandbyPool.append( tempStandbyTerm );
00355 }
00356
00357 in >> tempAllTerms;
00358 in >> tempInitQuizLength >> tempInitTermCount >> tempCurrTermPool >> tempCurrTerm;
00359
00360 scheduler.quizFirstLang = tempFirstLanguage;
00361 scheduler.quizTestLang = tempTestLanguage;
00362 scheduler.termPool = tempTermPool;
00363 scheduler.standbyPool = tempStandbyPool;
00364 scheduler.allTerms = tempAllTerms;
00365 scheduler.initQuizLength = tempInitQuizLength;
00366 scheduler.initTermCount = tempInitTermCount;
00367 scheduler.currTermPool = tempCurrTermPool;
00368 scheduler.currTerm = tempCurrTerm;
00369
00370 return( in );
00371 }