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 /* QDataStream::Qt_3 ? */ );
00072 
00073     // 0x0009 means 0.9.x version.  
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 ); // To mark the end of the pool.
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 ); // To mark the end of the standbyPool.
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 }

Generated on Sun Mar 1 17:30:47 2009 for toMOTko by  doxygen 1.5.6