public function testDeletePid() { $reflectionClass = new ReflectionClass($this->_TMInstance); $reflectedMethod = $reflectionClass->getMethod('_killPids'); $reflectedMethod->setAccessible(true); $reflectionProperty = $reflectionClass->getProperty('_numProcessesMax'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($this->_TMInstance, 20); //TEST remove pid 2 ( seek and destroy ) //put a known pid into the list $this->_redisClient->getConnection()->sadd('ch_pid_set_p2', 2); //check for correctness $this->assertTrue($this->_redisClient->getConnection()->sismember('ch_pid_set_p2', 2)); $reflectedMethod->invokeArgs($this->_TMInstance, array(null, 2)); $list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p2'); $this->assertNotContains(2, $list); $config = parse_ini_file('tm_config.ini', true); //remove 3 elements from queue 1 $x = QueuesList::get($config['context_definitions']); // $old_len = $this->_redisClient->getConnection()->scard( $x->list[0]->pid_set_name ); // $reflectedMethod->invokeArgs( $this->_TMInstance, array( $x->list[0], 0, 3 ) ); // $new_len = $this->_redisClient->getConnection()->scard( $x->list[0]->pid_set_name ); // $this->assertEquals( 3, $old_len - $new_len ); // //put a known pid into the list // $this->_redisClient->getConnection()->sadd( 'ch_pid_set_p3', 99999 ); // //check for correctness // $this->assertTrue( $this->_redisClient->getConnection()->sismember( 'ch_pid_set_p3', 99999 ) ); // $reflectedMethod->invokeArgs( $this->_TMInstance, array( $x->list[2], 99999 ) ); // $list = $this->_redisClient->getConnection()->smembers( 'ch_pid_set_p2' ); // $this->assertNotContains( 99999, $list ); //remove 5 pids by balancing $old_len = $this->_redisClient->getConnection()->scard($x->list['P1']->pid_set_name); $old_len += $this->_redisClient->getConnection()->scard($x->list['P2']->pid_set_name); $old_len += $this->_redisClient->getConnection()->scard($x->list['P3']->pid_set_name); $reflectedMethod->invokeArgs($this->_TMInstance, array(null, 0, 5)); $new_len = $this->_redisClient->getConnection()->scard($x->list['P1']->pid_set_name); $new_len += $this->_redisClient->getConnection()->scard($x->list['P2']->pid_set_name); $new_len += $this->_redisClient->getConnection()->scard($x->list['P3']->pid_set_name); $this->assertEquals(5, $old_len - $new_len); //delete all pid in a list $reflectedMethod->invokeArgs($this->_TMInstance, array($x->list[2])); $new_len = $this->_redisClient->getConnection()->scard($x->list[2]->pid_set_name); $this->assertEquals(0, $new_len); //bad and stranges //remove a non existent pid from a list //there is not. expected: do nothing $old_len = $this->_redisClient->getConnection()->scard($x->list[1]->pid_set_name); $reflectedMethod->invokeArgs($this->_TMInstance, array($x->list[1], 99999)); $new_len = $this->_redisClient->getConnection()->scard($x->list[1]->pid_set_name); $this->assertEquals(0, $old_len - $new_len); $this->assertNotEquals(0, $new_len); //Bad invocation, expected: pid kill routine but pid not found ( do nothing, ignore third param ) $old_list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p1'); $old_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p2'); $old_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p3'); $this->assertNotContains(99999, $old_list); $reflectedMethod->invokeArgs($this->_TMInstance, array(null, 99999, 3)); $new_list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p1'); $new_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p2'); $new_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p3'); $this->assertNotContains(99999, $new_list); $this->assertEquals($new_list, $old_list); //expected kill pid 99999 from queue2 and not found ( do nothing, ignore third param ) $old_list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p1'); $this->assertNotContains(99999, $old_list); $reflectedMethod->invokeArgs($this->_TMInstance, array($x->list[0], 99999, 3)); $new_list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p1'); $this->assertNotContains(99999, $new_list); //expected kill ALL pids and exit from infinite loop $old_list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p1'); $old_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p2'); $old_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p3'); $this->assertNotEmpty($old_list); $this->assertLessThan(3000, count($old_list)); $reflectedMethod->invokeArgs($this->_TMInstance, array(null, 0, 3000)); $new_list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p1'); $new_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p2'); $new_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p3'); $this->assertEmpty($new_list); //empty all queues $this->setUp(); //remove second set $this->_redisClient->getConnection()->del('ch_pid_set_p2'); $old_list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p1'); $old_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p3'); $this->assertNotEmpty($old_list); $reflectedMethod->invokeArgs($this->_TMInstance, array()); $new_list = $this->_redisClient->getConnection()->smembers('ch_pid_set_p1'); $new_list += $this->_redisClient->getConnection()->smembers('ch_pid_set_p3'); $this->assertEmpty($new_list); }
public function doAction() { try { $this->_checkData(); } catch (Exception $e) { if ($e->getCode() == -1) { Utils::sendErrMailReport($e->getMessage()); } Log::doLog($e->getMessage()); return $e->getCode(); } //check tag mismatch //get original source segment, first $segment = getSegment($this->id_segment); //compare segment-translation and get results $check = new QA($segment['segment'], $this->translation); $check->performConsistencyCheck(); if ($check->thereAreWarnings()) { $err_json = $check->getWarningsJSON(); $translation = $this->translation; } else { $err_json = ''; $translation = $check->getTrgNormalized(); } /* * begin stats counter * * It works good with default InnoDB Isolation level * * REPEATABLE-READ offering a row level lock for this id_segment * */ $db = Database::obtain(); $db->begin(); $old_translation = getCurrentTranslation($this->id_job, $this->id_segment); if (false === $old_translation) { $old_translation = array(); } // $old_translation if `false` sometimes //if volume analysis is not enabled and no translation rows exists //create the row if (!INIT::$VOLUME_ANALYSIS_ENABLED && empty($old_translation['status'])) { $_Translation = array(); $_Translation['id_segment'] = (int) $this->id_segment; $_Translation['id_job'] = (int) $this->id_job; $_Translation['status'] = Constants_TranslationStatus::STATUS_NEW; $_Translation['segment_hash'] = $segment['segment_hash']; $_Translation['translation'] = $segment['segment']; $_Translation['standard_word_count'] = $segment['raw_word_count']; $_Translation['serialized_errors_list'] = ''; $_Translation['suggestion_position'] = 0; $_Translation['warning'] = false; $_Translation['translation_date'] = date("Y-m-d H:i:s"); $res = addTranslation($_Translation); if ($res < 0) { $this->result['errors'][] = array("code" => -101, "message" => "database errors"); $db->rollback(); return $res; } /* * begin stats counter * */ $old_translation = $_Translation; } $_Translation = array(); $_Translation['id_segment'] = $this->id_segment; $_Translation['id_job'] = $this->id_job; $_Translation['status'] = $this->status; $_Translation['time_to_edit'] = $this->time_to_edit; $_Translation['translation'] = preg_replace('/[ \\t\\n\\r\\0\\x0A\\xA0]+$/u', '', $translation); $_Translation['serialized_errors_list'] = $err_json; $_Translation['suggestion_position'] = $this->chosen_suggestion_index; $_Translation['warning'] = $check->thereAreWarnings(); $_Translation['translation_date'] = date("Y-m-d H:i:s"); /** * Evaluate new Avg post-editing effort for the job: * - get old translation * - get suggestion * - evaluate $_seg_oldPEE and normalize it on the number of words for this segment * * - get new translation * - evaluate $_seg_newPEE and normalize it on the number of words for this segment * * - get $_jobTotalPEE * - evaluate $_jobTotalPEE - $_seg_oldPEE + $_seg_newPEE and save it into the job's row */ $this->updateJobPEE($old_translation, $_Translation); $editLogModel = new EditLog_EditLogModel($this->id_job, $this->password); $this->result['pee_error_level'] = $editLogModel->getMaxIssueLevel(); /** * when the status of the translation changes, the auto propagation flag * must be removed */ if ($_Translation['translation'] != $old_translation['translation'] || $this->status == Constants_TranslationStatus::STATUS_TRANSLATED || $this->status == Constants_TranslationStatus::STATUS_APPROVED) { $_Translation['autopropagated_from'] = 'NULL'; } $res = CatUtils::addSegmentTranslation($_Translation); if (!empty($res['errors'])) { $this->result['errors'] = $res['errors']; $msg = "\n\n Error addSegmentTranslation \n\n Database Error \n\n " . var_export(array_merge($this->result, $_POST), true); Log::doLog($msg); Utils::sendErrMailReport($msg); $db->rollback(); return -1; } if (INIT::$DQF_ENABLED && !empty($this->jobData['dqf_key']) && $_Translation['status'] == Constants_TranslationStatus::STATUS_TRANSLATED) { $dqfSegmentStruct = DQF_DqfSegmentStruct::getStruct(); if ($old_translation['suggestion'] == null) { $dqfSegmentStruct->target_segment = ""; $dqfSegmentStruct->tm_match = 0; } else { $dqfSegmentStruct->target_segment = $old_translation['suggestion']; $dqfSegmentStruct->tm_match = $old_translation['suggestion_match']; } $dqfSegmentStruct->task_id = $this->id_job; $dqfSegmentStruct->segment_id = $this->id_segment; $dqfSegmentStruct->source_segment = $segment['segment']; $dqfSegmentStruct->new_target_segment = $_Translation['translation']; $dqfSegmentStruct->time = $_Translation['time_to_edit']; // $dqfSegmentStruct->mtengine = $this->jobData['id_mt_engine']; $dqfSegmentStruct->mt_engine_version = 1; try { $dqfQueueHandler = new DqfQueueHandler(); $dqfQueueHandler->createSegment($dqfSegmentStruct); } catch (Exception $exn) { $msg = $exn->getMessage() . "\n\n" . $exn->getTraceAsString(); Log::doLog($msg); Utils::sendErrMailReport($msg); } } $propagateToTranslated = true; //for the moment, this is set explicitely if ($this->propagate == false) { $propagateToTranslated = false; } //propagate translations $TPropagation = array(); //Warning: this value will NOT be used to update values, //but to exclude current segment from auto-propagation $_idSegment = $this->id_segment; $propagateToTranslated ? $TPropagation['status'] = $this->status : null; $TPropagation['id_job'] = $this->id_job; $TPropagation['translation'] = $translation; $TPropagation['autopropagated_from'] = $this->id_segment; $TPropagation['serialized_errors_list'] = $err_json; $TPropagation['warning'] = $check->thereAreWarnings(); // $TPropagation[ 'translation_date' ] = date( "Y-m-d H:i:s" ); $TPropagation['segment_hash'] = $old_translation['segment_hash']; $propagationTotal = array(); if ($propagateToTranslated && in_array($this->status, array(Constants_TranslationStatus::STATUS_TRANSLATED, Constants_TranslationStatus::STATUS_APPROVED, Constants_TranslationStatus::STATUS_REJECTED))) { try { $propagationTotal = propagateTranslation($TPropagation, $this->jobData, $_idSegment, $propagateToTranslated); } catch (Exception $e) { $msg = $e->getMessage() . "\n\n" . $e->getTraceAsString(); Log::doLog($msg); Utils::sendErrMailReport($msg); $db->rollback(); return $e->getCode(); } } $old_wStruct = $this->recountJobTotals($old_translation['status']); //redundant because the update is made only where status = old status if ($this->status != $old_translation['status']) { //cambiato status, sposta i conteggi $old_count = !is_null($old_translation['eq_word_count']) ? $old_translation['eq_word_count'] : $segment['raw_word_count']; //if there is not a row in segment_translations because volume analysis is disabled //search for a just created row $old_status = !empty($old_translation['status']) ? $old_translation['status'] : Constants_TranslationStatus::STATUS_NEW; $counter = new WordCount_Counter($old_wStruct); $counter->setOldStatus($old_status); $counter->setNewStatus($this->status); $newValues = array(); $newValues[] = $counter->getUpdatedValues($old_count); foreach ($propagationTotal as $__pos => $old_value) { $counter->setOldStatus($old_value['status']); $counter->setNewStatus($this->status); $newValues[] = $counter->getUpdatedValues($old_value['total']); } try { $newTotals = $counter->updateDB($newValues); } catch (Exception $e) { $this->result['errors'][] = array("code" => -101, "message" => "database errors"); Log::doLog("Lock: Transaction Aborted. " . $e->getMessage()); $db->rollback(); return $e->getCode(); } } else { $newTotals = $old_wStruct; } //update total time to edit try { updateTotalTimeToEdit($this->id_job, $this->password, $this->time_to_edit); } catch (Exception $e) { $this->result['errors'][] = array("code" => -101, "message" => "database errors"); Log::doLog("Lock: Transaction Aborted. " . $e->getMessage()); // $x1 = explode( "\n" , var_export( $old_translation, true) ); // Log::doLog("Lock: Translation status was " . implode( " ", $x1 ) ); $db->rollback(); return $e->getCode(); } $job_stats = CatUtils::getFastStatsForJob($newTotals); $project = getProject($this->jobData['id_project']); $project = array_pop($project); $job_stats['ANALYSIS_COMPLETE'] = $project['status_analysis'] == Constants_ProjectStatus::STATUS_DONE || $project['status_analysis'] == Constants_ProjectStatus::STATUS_NOT_TO_ANALYZE ? true : false; $file_stats = array(); $this->result['stats'] = $job_stats; $this->result['file_stats'] = $file_stats; $this->result['code'] = 1; $this->result['data'] = "OK"; $this->result['version'] = date_create($_Translation['translation_date'])->getTimestamp(); /* FIXME: added for code compatibility with front-end. Remove. */ $_warn = $check->getWarnings(); $warning = $_warn[0]; /* */ $this->result['warning']['cod'] = $warning->outcome; if ($warning->outcome > 0) { $this->result['warning']['id'] = $this->id_segment; } else { $this->result['warning']['id'] = 0; } //strtoupper transforms null to "" so check for the first element to be an empty string if (!empty($this->split_statuses[0]) && !empty($this->split_num)) { /* put the split inside the transaction if they are present */ $translationStruct = TranslationsSplit_SplitStruct::getStruct(); $translationStruct->id_segment = $this->id_segment; $translationStruct->id_job = $this->id_job; $translationStruct->target_chunk_lengths = array('len' => $this->split_chunk_lengths, 'statuses' => $this->split_statuses); $translationDao = new TranslationsSplit_SplitDAO(Database::obtain()); $result = $translationDao->update($translationStruct); } $db->commit(); //EVERY time an user changes a row in his job when the job is completed, // a query to do the update is executed... // Avoid this by setting a key on redis with an reasonable TTL $redisHandler = new RedisHandler(); $job_status = $redisHandler->getConnection()->get('job_completeness:' . $this->id_job); if ($job_stats['TRANSLATED_PERC'] == '100' && empty($job_status)) { $redisHandler->getConnection()->setex('job_completeness:' . $this->id_job, 60 * 60 * 24 * 15, true); //15 days $update_completed = setJobCompleteness($this->id_job, 1); if ($update_completed < 0) { $msg = "\n\n Error setJobCompleteness \n\n " . var_export($_POST, true); $redisHandler->getConnection()->del('job_completeness:' . $this->id_job); Log::doLog($msg); Utils::sendErrMailReport($msg); } } }