jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/398651 )
Change subject: Introduce ScoreStorage and its Sql implementetion
......................................................................
Introduce ScoreStorage and its Sql implementetion
It will take over some works of the Cache.php
Bug: T181334
Change-Id: I94025f3ee38ee723a4f9e8c08d353be27b3d7558
---
M includes/ServiceWiring.php
A includes/Storage/ScoreStorage.php
A includes/Storage/SqlScoreStorage.php
A tests/phpunit/includes/Storage/SqlScoreStorageTest.php
4 files changed, 304 insertions(+), 1 deletion(-)
Approvals:
jenkins-bot: Verified
Awight: Looks good to me, approved
diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php
index 458c902..0d114ad 100644
--- a/includes/ServiceWiring.php
+++ b/includes/ServiceWiring.php
@@ -19,6 +19,7 @@
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use ORES\Storage\SqlModelLookup;
+use ORES\Storage\SqlScoreStorage;
return [
'ORESModelLookup' => function ( MediaWikiServices $services ) {
@@ -31,6 +32,13 @@
$services->getMainWANObjectCache(),
LoggerFactory::getInstance( 'ORES' )
);
- }
+ },
+
+ 'ORESScoreStorage' => function ( MediaWikiServices $services ) {
+ return new SqlScoreStorage(
+ $services->getDBLoadBalancer(),
+ $services->getService( 'ORESModelLookup' )
+ );
+ },
];
diff --git a/includes/Storage/ScoreStorage.php
b/includes/Storage/ScoreStorage.php
new file mode 100644
index 0000000..fe9a79c
--- /dev/null
+++ b/includes/Storage/ScoreStorage.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace ORES\Storage;
+
+/**
+ * Service interface to store score data in storage.
+ *
+ * @license GPL-2.0+
+ */
+interface ScoreStorage {
+
+ /**
+ * Save scores to the database
+ *
+ * @param array[] $scores in the same structure as is returned by ORES.
+ * @param callable $errorCallback This callback is called when we
cannot parse a revision score
+ * response. The signature is errorCallback( string
$errorMessage, string $revisionID )
+ */
+ public function storeScores( $scores, callable $errorCallback = null );
+
+}
diff --git a/includes/Storage/SqlScoreStorage.php
b/includes/Storage/SqlScoreStorage.php
new file mode 100644
index 0000000..472faa4
--- /dev/null
+++ b/includes/Storage/SqlScoreStorage.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace ORES\Storage;
+
+use InvalidArgumentException;
+use ORES\Parser\ScoreParser;
+use RuntimeException;
+use Wikimedia\Rdbms\LoadBalancer;
+
+class SqlScoreStorage implements ScoreStorage {
+
+ private $loadBalancer;
+
+ private $modelLookup;
+
+ public function __construct(
+ LoadBalancer $loadBalancer,
+ ModelLookup $modelLookup
+ ) {
+ $this->loadBalancer = $loadBalancer;
+ $this->modelLookup = $modelLookup;
+ }
+
+ /**
+ * @see ModelLookup::getModelId()
+ *
+ * @param array[] $scores
+ * @param callable $errorCallback
+ * @return int
+ */
+ public function storeScores( $scores, callable $errorCallback = null ) {
+ // TODO: Make it an argument and deprecate the whole config
variable
+ global $wgOresModelClasses;
+
+ if ( $errorCallback === null ) {
+ $errorCallback = function ( $mssg, $revision ) {
+ throw new RuntimeException( "Model contains an
error for $revision: $mssg" );
+ };
+ }
+
+ $dbData = [];
+
+ $scoreParser = new ScoreParser( $this->modelLookup,
$wgOresModelClasses );
+ foreach ( $scores as $revision => $revisionData ) {
+ try {
+ $dbDataPerRevision =
$scoreParser->processRevision( $revision, $revisionData );
+ } catch ( InvalidArgumentException $exception ) {
+ call_user_func( $errorCallback,
$exception->getMessage(), $revision );
+ continue;
+ }
+
+ $dbData = array_merge( $dbData, $dbDataPerRevision );
+ }
+
+ $this->loadBalancer->getConnection( DB_MASTER )->insert(
+ 'ores_classification',
+ $dbData,
+ __METHOD__,
+ [ 'IGNORE' ]
+ );
+ }
+
+}
diff --git a/tests/phpunit/includes/Storage/SqlScoreStorageTest.php
b/tests/phpunit/includes/Storage/SqlScoreStorageTest.php
new file mode 100644
index 0000000..12dd8af
--- /dev/null
+++ b/tests/phpunit/includes/Storage/SqlScoreStorageTest.php
@@ -0,0 +1,183 @@
+<?php
+
+namespace ORES\Tests;
+
+use MediaWiki\MediaWikiServices;
+use MediaWikiLangTestCase;
+use MWException;
+use ORES\Storage\HashModelLookup;
+use ORES\Storage\SqlScoreStorage;
+
+/**
+ * @group ORES
+ * @group Database
+ * @covers ORES\Storage\SqlScoreStorage
+ */
+class SqlScoreStorageTest extends MediaWikiLangTestCase {
+
+ const GOODFAITH = 1;
+ const REVERTED = 2;
+ const DAMAGING = 3;
+
+ /**
+ * @var SqlScoreStorage
+ */
+ protected $storage;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->tablesUsed[] = 'ores_classification';
+ $modelData = [
+ 'reverted' => [ 'id' => self::REVERTED, 'version' =>
'0.0.1' ],
+ 'damaging' => [ 'id' => self::DAMAGING, 'version' =>
'0.0.2' ],
+ 'goodfaith' => [ 'id' => self::GOODFAITH, 'version' =>
'0.0.3' ],
+ ];
+ $this->storage = new SqlScoreStorage(
+ MediaWikiServices::getInstance()->getDBLoadBalancer(),
+ new HashModelLookup( $modelData )
+ );
+ }
+
+ public function storeScoresProvider() {
+ return [
+ [
+ [
+ 'wiki' => [
+ 'models' => [
+ 'damaging' => [
+ 'version' =>
'0.0.2',
+ ],
+ 'goodfaith' => [
+ 'version' =>
'0.0.3',
+ ],
+ ],
+ 'scores' => [
+ '12345' => [
+ 'damaging' => [
+ 'score'
=> [
+
'prediction' => false,
+
'probability' => [
+
'false' => 0.933,
+
'true' => 0.067,
+
],
+ ],
+ ],
+ 'reverted' => [
+ 'score'
=> [
+
'prediction' => true,
+
'probability' => [
+
'false' => 0.124,
+
'true' => 0.876,
+
],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ [
+ (object)[
+ 'oresc_rev' => '12345',
+ 'oresc_model' =>
(string)self::REVERTED,
+ 'oresc_class' => '1',
+ 'oresc_probability' => '0.876',
+ 'oresc_is_predicted' => '1'
+ ],
+ (object)[
+ 'oresc_rev' => '12345',
+ 'oresc_model' =>
(string)self::DAMAGING,
+ 'oresc_class' => '1',
+ 'oresc_probability' => '0.067',
+ 'oresc_is_predicted' => '0'
+ ],
+ ],
+ [ 12345 ],
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider storeScoresProvider
+ */
+ public function testStoreScores( $scores, $expected, $revIds ) {
+ $this->setMwGlobals( [
+ 'wgOresWikiId' => 'wiki',
+ ] );
+
+ $scores = $scores['wiki']['scores'];
+
+ $this->storage->storeScores( $scores );
+
+ $dbr = \wfGetDB( DB_REPLICA );
+ $res = $dbr->select(
+ 'ores_classification',
+ [
+ 'oresc_rev',
+ 'oresc_model',
+ 'oresc_class',
+ 'oresc_probability',
+ 'oresc_is_predicted'
+ ],
+ [ 'oresc_rev' => $revIds ],
+ __METHOD__,
+ 'ORDER BY oresc_probability'
+ );
+
+ $this->assertEquals( $expected, iterator_to_array( $res, false
) );
+ }
+
+ public function storeScoresInvalidProvider() {
+ return [
+ [
+ [ 1111 => [
+ 'non existing model' => [
+ 'score' => [
+ 'prediction' => true,
+ 'probability' => [
'true' => 0.9, 'false' => 0.1 ]
+ ],
+ ],
+ ] ]
+ ],
+ [
+ [ 12345 => [
+ 'damaging' => [
+ 'error' => [
+ 'message' => 'YAY, it
failed',
+ ],
+ ],
+ 'reverted' => [
+ 'score' => [
+ 'prediction' => false,
+ 'probability' => [
'true' => 0.3, 'false' => 0.7 ]
+ ],
+ ]
+ ] ]
+ ],
+ [
+ [ 12345 => [
+ 'reverted' => [
+ 'score' => [
+ 'prediction' => false,
+ 'probability' => [ 'It
should fail' => 0.3, 'false' => 0.7 ]
+ ],
+ ]
+ ] ]
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider storeScoresInvalidProvider
+ */
+ public function testProcessRevisionInvalid( array $data ) {
+ $this->setExpectedException( MWException::class,
'processRevisionInvalid failure' );
+ $this->storage->storeScores(
+ $data,
+ function () {
+ throw new MWException( 'processRevisionInvalid
failure' );
+ }
+ );
+ }
+
+}
--
To view, visit https://gerrit.wikimedia.org/r/398651
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I94025f3ee38ee723a4f9e8c08d353be27b3d7558
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/extensions/ORES
Gerrit-Branch: master
Gerrit-Owner: Ladsgroup <[email protected]>
Gerrit-Reviewer: Awight <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Ladsgroup <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Thiemo Kreuz (WMDE) <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits