static function updateRanking() { $qpru = QuizPendingRankingUpdate::fetchAll(); if( $qpru ) { $benchMessage = $benchSQLStart = $benchSQLEnd = $benchCalculusStart = $benchCalculusEnd = $benchTotalStart = $benchTotalEnd = null; // We only update one quiz application's leaderboard on each // cronjob pass even if multiple leaderboards are awaiting update // to limit SQL queries $lowestRanking = (int)$qpru[0]['lowest_global_ranking']; $clusterIdentifier = $qpru[0]['cluster_identifier']; $applicationId = (int)$qpru[0]['application_id']; $application = CacheApplicationTool::buildLocalizedApplicationByApplication($applicationId); if( is_numeric( $lowestRanking ) && $lowestRanking != null && is_numeric( $applicationId ) && $applicationId != null ) { $benchTotalStart = getrusage(); $db = eZDB::instance(); $db->begin(); $results = $db->arrayQuery( sprintf(" SELECT uuid, user_specialty, score, global_ranking, specialty_ranking FROM %s WHERE global_ranking <= %d AND cluster_identifier = '%s' AND application_id = %d AND ( nb_correct > 0 OR nb_wrong > 0 ) ORDER BY score DESC", self::SQL_TABLENAME, $lowestRanking, $db->escapeString( $clusterIdentifier ), $applicationId ) ); $updateCount = 0; if( $results && count( $results ) > 1 ) { $benchCalculusStart = getrusage(); $updateResult = null; $spe = $changed = $res = array(); $r = $_r = 1; foreach( $results as $rank => $row ) { if( (int)$row['global_ranking'] != $r ) { $changed[] = $row['uuid']; $results[$rank]['global_ranking'] = $r; } if( isset( $results[$rank + 1] ) && ( (int)$results[$rank + 1]['score'] != (int)$row['score'] ) ) $r++; $spe[$row['user_specialty']][] = $results[$rank]; $res[$row['uuid']] = $rank; } foreach( $spe as $row ) { $_r = 1; if( count( $row ) > 1 ) { foreach( $row as $_rank => $_row ) { if( (int)$_row['specialty_ranking'] != $_r ) { if( !in_array( $_row['uuid'], $changed ) ) $changed[] = $_row['uuid']; $results[$res[$_row['uuid']]]['specialty_ranking'] = $_r; } if( isset( $row[$_rank + 1] ) ) { if( (int)$row[$_rank + 1]['score'] != (int)$_row['score'] ) $_r++; } } } else { if( (int)$row[0]['specialty_ranking'] != $_r ) { if( !in_array( $row[0]['uuid'], $changed ) ) $changed[] = $row[0]['uuid']; $results[$res[$row[0]['uuid']]]['specialty_ranking'] = $_r; } } } $benchCalculusEnd = getrusage(); if( count( $changed ) > 0 ) { $benchSQLStart = getrusage(); foreach( $changed as $uuid ) { $updateResult = $db->query( sprintf(" UPDATE %s SET global_ranking = %d, specialty_ranking = %d WHERE uuid = '%s' AND cluster_identifier = '%s' AND application_id = %d", self::SQL_TABLENAME, $results[$res[$uuid]]['global_ranking'], $results[$res[$uuid]]['specialty_ranking'], $uuid, $db->escapeString( $clusterIdentifier ), $applicationId ) ); } $benchSQLEnd = getrusage(); } $updateCount = count( $changed ); $benchTotalEnd = getrusage(); if( $updateCount > 0 ) $benchMessage = 'Total: ' . self::benchmarckTime( $benchTotalEnd, $benchTotalStart ) . 'ms (Calculus: ' . self::benchmarckTime( $benchCalculusEnd, $benchCalculusStart ) . 'ms, SQL: ' . self::benchmarckTime( $benchSQLEnd, $benchSQLStart ) . 'ms)'; } else { if( $results && count( $results ) == 1 ) { $updateResult = $db->query( sprintf(" UPDATE %s SET global_ranking = 1, specialty_ranking = 1 WHERE uuid = '%s' AND cluster_identifier = '%s' AND application_id = %d", self::SQL_TABLENAME, $results[0]['uuid'], $db->escapeString( $clusterIdentifier ), $applicationId ) ); $updateCount = 1; } } $db->commit(); } // Once leaderboard update is done, we delete the corresponding PendingRankingUpdate row in DB QuizPendingRankingUpdate::removeById( (int)$qpru[0]['id'] ); // If we updated a user with a lower global ranking than the one used for the recalculation above, that means we // had users sharing the same global ranking. We need to schedule yet another recalculation with the lowest ranking available if( $r != $lowestRanking ) { ClusterTool::setCurrentCluster( $clusterIdentifier ); $lowestGlobalRanking = QuizPlayerScoring::fetchBy( array( 'cluster_identifier' => $clusterIdentifier, 'application_id' => $applicationId ), array( 'global_ranking' => 'desc' ), array( 'length' => 1 ) ); QuizPendingRankingUpdate::add( $lowestGlobalRanking[0]->attribute('global_ranking'), $applicationId ); } return array( 'updateCount' => $updateCount, 'updateResult' => $updateResult, 'clusterIdentifier' => $clusterIdentifier, 'applicationId' => $applicationId, 'benchmark' => $benchMessage ); } return null; }
if (is_null($lastGlobalRanking)) { $lastGlobalRanking = $r[0]; continue; } if (count(array_unique($r)) > 1 || $r[0] <= $lastGlobalRanking) { $shouldRecalculate = true; break; } $lastGlobalRanking = $r[0]; } if ($shouldRecalculate) { $lowestGlobalranking = QuizPlayerScoring::fetchBy( array('application_id' => $applicationId), array('global_ranking' => 'desc'), array('length' => 1) ); QuizPendingRankingUpdate::add( (int)$lowestGlobalranking[0]->attribute('global_ranking'), $applicationId ); $message = "Scheduled a complete ranking update for application with id #$applicationId ($clusterIdentifier)"; $cli->output($message); eZLog::write($message, 'quiz.log'); } } } }
foreach( $playerScoringRows as $playerScoring ) { $user = MMUsers::fetchByIdAndCountry( $playerScoring->attribute( 'uuid' ), $options['countryCode'] ); if( $user ) { if( isset( $us[$user->attribute( 'user_speciality' )] ) && $us[$user->attribute( 'user_speciality' )]['id'] != $playerScoring->attribute( 'user_specialty' ) ) { if( !isset( $lowestRankingsPerQuizApp[$playerScoring->attribute( 'application_id' )] ) || ( isset( $lowestRankingsPerQuizApp[$playerScoring->attribute( 'application_id' )] ) && $lowestRankingsPerQuizApp[$playerScoring->attribute( 'application_id' )] < (int)$playerScoring->attribute( 'global_ranking' ) ) ) $lowestRankingsPerQuizApp[$playerScoring->attribute( 'application_id' )] = (int)$playerScoring->attribute( 'global_ranking' ); $oldUS = $playerScoring->attribute( 'user_specialty' ); $playerScoring->setAttribute( 'user_specialty', $us[$user->attribute( 'user_speciality' )]['id'] ); $playerScoring->store(); $updated++; $cli->output( "Updated user speciality (" . $oldUS . " -> " . $us[$user->attribute( 'user_speciality' )]['id'] . ") in scoring data for user " . $playerScoring->attribute( 'uuid' ) ); } } } } if( $updated > 0 ) { $cli->output( "Alignment process finished, $updated row(s) updated and scheduled ranking update for " . count( $lowestRankingsPerQuizApp ) . " application(s)" ); foreach( $lowestRankingsPerQuizApp as $applicationId => $lowestRanking ) QuizPendingRankingUpdate::add( $lowestRanking, $applicationId ); } else $cli->output( "All clear, nothing to repair" ); } else { $cli->output( "Please specify the clusterIdentifier and countryCode parameters" ); } $script->shutdown();
/** * @param array $uncrytedTicket * @return MMUsers */ public static function createOrUpdateMMUser( $uncrytedTicket ) { $userId = $uncrytedTicket['uuid']; $customerType = $uncrytedTicket['customerType']; if ( isset($uncrytedTicket['userSpeciality']) ) { $userSpeciality = $uncrytedTicket['userSpeciality']; } else { $userSpeciality = $uncrytedTicket['mainSpeciality']; } $state = $uncrytedTicket['state']; $country = eZIni::instance('site.ini')->variable('RegionalSettings','CountryOfRegistration'); $language = $uncrytedTicket['language']; /** * @todo: check $language entry format and parse it if needed to get an iso code on 2 chars */ $iniMapping = eZINI::instance('mercktaxonomymapping.ini'); $taxonomyType = $iniMapping->variable( 'Taxonomy', 'SpecialtiesTaxonomyType'); $specialtiesMappingINI = $iniMapping->variable( 'Taxonomy', 'Specialties' ); $customerTypesMappingINI = $iniMapping->variable( 'Taxonomy', 'CustomerTypes' ); if($taxonomyType != "transitive") { if ( isset( $customerTypesMappingINI[$customerType] ) ) { $customerType = $customerTypesMappingINI[$customerType]; } else { $customerType = $customerTypesMappingINI['Default']; self::updateDBWithEmptyValue( 'empty-CT', $userId, $country ); } if ( isset( $specialtiesMappingINI[$userSpeciality] ) ) { $userSpeciality = $specialtiesMappingINI[$userSpeciality]; } else { $userSpeciality = $specialtiesMappingINI['Default']; self::updateDBWithEmptyValue( 'empty-SPE', $userId, $country ); } } else { $customerTypesMapping = SolrSafeOperatorHelper::getCustomerTypes(); if ( !isset( $customerTypesMapping["$customerType"] ) ) { // fill with default value $customerType = $customerTypesMappingINI['Default']; self::updateDBWithEmptyValue( 'empty-CT', $userId, $country ); } $specialtiesMapping = SolrSafeOperatorHelper::getUserSpecialities(); if ( !isset( $specialtiesMapping[(string)$userSpeciality] ) ) { // fill with default value $userSpeciality = $specialtiesMappingINI['Default']; self::updateDBWithEmptyValue( 'empty-SPE', $userId, $country ); } } /** @var MMUsers $user */ $user = MMUsers::fetchByIdAndCountry( $userId, $country ); $isModifiedUser = false; if ( $user ) { if ( $customerType != $user->attribute('customer_type') ) { $user->setAttribute('customer_type', $customerType); $isModifiedUser = true; } if ( $userSpeciality !== $user->attribute('user_speciality') ) { $mainSpeciality = UserSpecialityContentSpeciality::getContentSpeciality ( $userSpeciality ); $defaultAdditionalSpecialities = MMUserLogin::getAddSpecialties($mainSpeciality); $user->setAttribute('user_speciality', $userSpeciality); // mapping main_spe additional spe $preferences = $user->getPreferences(); $userAditionalSpecialities = $preferences['specialities']; array_shift($userAditionalSpecialities); $tmp = array_diff( $userAditionalSpecialities, $defaultAdditionalSpecialities ); if( empty( $tmp ) ) // we don't modify the user spes if he has changed his selection { $preferences["specialities"] = MMUserLogin::getAddSpecialties($mainSpeciality); $user->setPreferences($preferences); } $user->setPreferences($preferences); // The user specialty has been modified, we need to update it for Quiz-type applications of the current cluster if( CacheApplicationTool::buildLocalizedApplicationByIdentifier('train-the-brain') instanceof ApplicationLocalized) { $playerScoring = QuizPlayerScoring::fetchOneBy( array( 'application_id' => (int)$app->applicationObject->id, 'uuid' => $user->attribute( 'uuid' ) ) ); // Checking if the current user has scored at least once for the application if( $playerScoring ) { foreach( FacetFilteringTool::getTaxoTranslationWithIDs( 'user_specialty' ) as $k => $t ) { if( ($k . "") === ($userSpeciality . "") ) { // Updating the user specialty id in the player scoring table $playerScoring->setAttribute( 'user_specialty', $t['id'] ); $playerScoring->store(); } } // Scheduling a ranking update QuizPendingRankingUpdate::add( $playerScoring->attribute( 'global_ranking' ), $app->applicationObject->id ); } } $isModifiedUser = true; } if ( $country != $user->attribute('country') ) { $user->setAttribute('country', $country); $isModifiedUser = true; } if( $language != $user->attribute( 'language' ) ) { $user->setAttribute('language', $language ); $isModifiedUser = true; } if ( $state != $user->attribute('state') ) { $user->setAttribute('state', $state); $isModifiedUser = true; } if ( $isModifiedUser ) { $user->setAttribute('date_update', time()); $user->store(); } // set gpnotebook hand shake data in preferences if($user->getGPNotebookHS()) { SolrSafeOperatorHelper::getAndStoreGPNotebookHS(); } } else { $mainSpeciality = UserSpecialityContentSpeciality::getContentSpeciality ( $userSpeciality ); $insertedArray = array ( 'uuid' => $userId, 'customer_type' => $customerType, 'user_speciality' => $userSpeciality, 'country' => $country, 'language' => $language, 'date_update' => time(), 'state' => $state, ); $user = new MMUsers ($insertedArray); $user->store(); // mapping main_spe additional spe $preferences = $user->getPreferences(); $preferences["specialities"] = MMUserLogin::getAddSpecialties($mainSpeciality); $user->setPreferences($preferences); $isModifiedUser = true; } if ( $isModifiedUser ) { // Tag Lyris user to be reimported : $user->tagLyrisToProcess(); } return $user; }