Lucas Werkmeister (WMDE) has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/366582 )

Change subject: Distinguish between non-mandatory and mandatory constraints
......................................................................

Distinguish between non-mandatory and mandatory constraints

This adds a new constraint status, “warning”, for all violations of
constraints that are not mandatory. “violation” is only used for
violations of constraints that are explicitly marked mandatory, using
the “constraint status: mandatory constraint” constraint parameter
(which ConstraintParameterParser is taught to parse).

To avoid having to change all checkers and duplicating the constraint
status parsing into them, DelegatingConstraintChecker downgrades
“violation” to “warning” if appropriate, so that the individual checkers
remain unchanged. A few tests that include DelegatingConstraintChecker
are adjusted to make their constraints mandatory so that the result is
still the expected “violation”.

On the special page, warnings are displayed in yellow; the status
“exception”, which previously used this color, is changed to a slightly
darker green than “compliance”. The gadget is slightly updated to
include warnings in the report, but the full update for the gadget
(different texts, different icon, …) will happen in a separate commit.

Bug: T164254
Change-Id: I6fa0af86dd7d3d00abc4cd004417667028599428
---
M extension.json
M i18n/en.json
M i18n/qqq.json
M includes/ConstraintCheck/DelegatingConstraintChecker.php
M includes/ConstraintCheck/Helper/ConstraintParameterParser.php
M includes/ConstraintCheck/Result/CheckResult.php
M modules/SpecialConstraintReportPage.css
M modules/gadget.js
M tests/phpunit/Api/CheckConstraintsTest.php
M tests/phpunit/DelegatingConstraintCheckerTest.php
M tests/phpunit/Helper/ConstraintParameterParserTest.php
M tests/phpunit/Specials/SpecialConstraintReportTest.php
12 files changed, 202 insertions(+), 5 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/WikibaseQualityConstraints
 refs/changes/82/366582/1

diff --git a/extension.json b/extension.json
index 3318802..eca145f 100644
--- a/extension.json
+++ b/extension.json
@@ -136,6 +136,16 @@
                        "description": "The property ID of the 'exception to 
constraint' property (data type: item), which specifies the exceptions of a 
constraint.",
                        "public": true
                },
+               "WBQualityConstraintsConstraintStatusId": {
+                       "value": "P2316",
+                       "description": "The property ID of the 'constraint 
status' property (data type: item), which specifies the constraint status of a 
constraint statement. Currently, only one constraint status is known (see 
WBQualityConstraintsMandatoryConstraintId), and the default status is signified 
by the absence of a 'constraint status' qualifier.",
+                       "public": true
+               },
+               "WBQualityConstraintsMandatoryConstraintId": {
+                       "value": "Q21502408",
+                       "description": "The item ID of the 'mandatory 
constraint' item, which, when used in a 'constraint status' qualifier of a 
'property constraint' statement on a property, indicates that the constraint is 
mandatory and should have no violations except for the known exceptions.",
+                       "public": true
+               },
                "WBQualityConstraintsDistinctValuesConstraintId": {
                        "value": "Q21502410",
                        "description": "The item ID of the 'distinct values 
constraint' item, which, when used in a 'property constraint' statement on a 
property, indicates that all values for this property should differ from each 
other, or, equivalently, that each value for this property should be unique to 
one item.",
diff --git a/i18n/en.json b/i18n/en.json
index 6f104b7..99a5ced 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -23,6 +23,7 @@
        "wbqc-constraintreport-status-todo": "Todo",
        "wbqc-constraintreport-status-bad-parameters": "Bad parameters",
        "wbqc-constraintreport-status-deprecated": "Deprecated",
+       "wbqc-constraintreport-status-warning": "Warning",
        "wbqc-constraintreport-result-table-header-status": "Status",
        "wbqc-constraintreport-result-table-header-claim": "Claim",
        "wbqc-constraintreport-result-table-header-constraint": "Constraint",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 071ea19..ff4ff75 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -25,6 +25,7 @@
        "wbqc-constraintreport-status-todo": "Status for constraints which 
cannot be checked yet.\n{{Identical|Todo}}",
        "wbqc-constraintreport-status-bad-parameters": "Status for constraints 
that have missing or invalid constraint parameters.",
        "wbqc-constraintreport-status-deprecated": "Status for constraint 
checks that have been skipped because the statement is deprecated.",
+       "wbqc-constraintreport-status-warning": "Status for statements that 
violate a non-mandatory constraint.",
        "wbqc-constraintreport-result-table-header-status": "Header of the 
column that tells whether the check found a violation or something 
else.\n{{Identical|Status}}",
        "wbqc-constraintreport-result-table-header-claim": "Header of the 
column that displays a link to the claim, the used property and its 
value.\n{{Identical|Claim}}",
        "wbqc-constraintreport-result-table-header-constraint": "Header of the 
column that gives information about which constraint was 
checked.\n{{Identical|Constraint}}",
diff --git a/includes/ConstraintCheck/DelegatingConstraintChecker.php 
b/includes/ConstraintCheck/DelegatingConstraintChecker.php
index d5b0a7c..15d55cd 100644
--- a/includes/ConstraintCheck/DelegatingConstraintChecker.php
+++ b/includes/ConstraintCheck/DelegatingConstraintChecker.php
@@ -3,6 +3,7 @@
 namespace WikibaseQuality\ConstraintReport\ConstraintCheck;
 
 use InvalidArgumentException;
+use LogicException;
 use MediaWiki\MediaWikiServices;
 use Wikibase\DataModel\Entity\EntityDocument;
 use Wikibase\DataModel\Entity\PropertyId;
@@ -328,6 +329,35 @@
                                ( microtime( true ) - $startTime ) * 1000
                        );
 
+                       try {
+                               $constraintStatus = 
$this->constraintParameterParser
+                                       ->parseConstraintStatusParameter( 
$constraint->getConstraintParameters() );
+                       } catch ( ConstraintParameterException $e ) {
+                               $result = new CheckResult(
+                                       $entity->getId(),
+                                       $statement,
+                                       $constraint,
+                                       [],
+                                       CheckResult::STATUS_BAD_PARAMETERS,
+                                       $e->getMessage()
+                               );
+                               $constraintStatus = null;
+                       }
+                       if ( $constraintStatus === null ) {
+                               // downgrade violation to warning
+                               if ( $result->getStatus() === 
CheckResult::STATUS_VIOLATION ) {
+                                       $result->setStatus( 
CheckResult::STATUS_WARNING );
+                               }
+                       } else {
+                               if ( $constraintStatus !== 'mandatory' ) {
+                                       throw new LogicException(
+                                               "Unknown constraint status 
'$constraintStatus', " .
+                                               "only known status is 
'mandatory'"
+                                       );
+                               }
+                               $result->getParameters['constraint_status'] = 
$constraintStatus;
+                       }
+
                        return $result;
                } else {
                        return new CheckResult( $entity->getId(), $statement, 
$constraint, [], CheckResult::STATUS_TODO, null );
@@ -349,6 +379,7 @@
                        $order = [
                                'bad-parameters' => $orderNum++,
                                'violation' => $orderNum++,
+                               'warning' => $orderNum++,
                                'exception' => $orderNum++,
                                'compliance' => $orderNum++,
                                'deprecated' => $orderNum++,
diff --git a/includes/ConstraintCheck/Helper/ConstraintParameterParser.php 
b/includes/ConstraintCheck/Helper/ConstraintParameterParser.php
index d1423d7..f95f82c 100644
--- a/includes/ConstraintCheck/Helper/ConstraintParameterParser.php
+++ b/includes/ConstraintCheck/Helper/ConstraintParameterParser.php
@@ -708,4 +708,55 @@
                }
        }
 
+       private function parseConstraintStatusParameterFromStatement( array 
$constraintParameters ) {
+               $constraintStatusId = $this->config->get( 
'WBQualityConstraintsConstraintStatusId' );
+               $mandatoryId = $this->config->get( 
'WBQualityConstraintsMandatoryConstraintId' );
+               $this->requireSingleParameter( $constraintParameters, 
$constraintStatusId );
+               $snak = $this->snakDeserializer->deserialize( 
$constraintParameters[$constraintStatusId][0] );
+               $this->requireValueParameter( $snak, $constraintStatusId );
+               $statusId = 
$snak->getDataValue()->getEntityId()->getSerialization();
+
+               if ( $statusId === $mandatoryId ) {
+                       return 'mandatory';
+               } else {
+                       throw new ConstraintParameterException(
+                               wfMessage( 
'wbqc-violation-message-parameter-oneof' )
+                                       ->rawParams( 
$this->constraintParameterRenderer->formatPropertyId( $constraintStatusId, 
Role::CONSTRAINT_PARAMETER_PROPERTY ) )
+                                       ->numParams( 1 )
+                                       ->rawParams( 
$this->constraintParameterRenderer->formatItemIdList( [ $mandatoryId ], 
Role::CONSTRAINT_PARAMETER_VALUE ) )
+                                       ->escaped()
+                       );
+               }
+       }
+
+       private function parseConstraintStatusParameterFromTemplate( array 
$constraintParameters ) {
+               if ( $constraintParameters['constraint_status'] === 'mandatory' 
) {
+                       return 'mandatory';
+               } else {
+                       throw new ConstraintParameterException(
+                               wfMessage( 
'wbqc-violation-message-parameter-oneof' )
+                                       ->rawParams( 
$this->constraintParameterRenderer->formatPropertyId( 'constraint_status', 
Role::CONSTRAINT_PARAMETER_PROPERTY ) )
+                                       ->numParams( 1 )
+                                       ->rawParams( 
$this->constraintParameterRenderer->formatItemIdList( [ 'mandatory' ], 
Role::CONSTRAINT_PARAMETER_VALUE ) )
+                                       ->escaped()
+                       );
+               }
+       }
+
+       /**
+        * @param array $constraintParameters see {@link 
\WikibaseQuality\Constraint::getConstraintParameters()}
+        * @throws ConstraintParameterException if the parameter is invalid
+        * @return string|null 'mandatory' or null
+        */
+       public function parseConstraintStatusParameter( array 
$constraintParameters ) {
+               $constraintStatusId = $this->config->get( 
'WBQualityConstraintsConstraintStatusId' );
+               if ( array_key_exists( $constraintStatusId, 
$constraintParameters ) ) {
+                       return 
$this->parseConstraintStatusParameterFromStatement( $constraintParameters );
+               } elseif ( array_key_exists( 'constraint_status', 
$constraintParameters ) ) {
+                       return 
$this->parseConstraintStatusParameterFromTemplate( $constraintParameters );
+               } else {
+                       return null;
+               }
+       }
+
 }
diff --git a/includes/ConstraintCheck/Result/CheckResult.php 
b/includes/ConstraintCheck/Result/CheckResult.php
index 355354f..d1bbbff 100644
--- a/includes/ConstraintCheck/Result/CheckResult.php
+++ b/includes/ConstraintCheck/Result/CheckResult.php
@@ -44,6 +44,13 @@
         * The constraint has not been checked because the statement is 
deprecated.
         */
        const STATUS_DEPRECATED = 'deprecated';
+       /**
+        * The statement violates the constraint, but the constraint is not 
mandatory.
+        *
+        * DelegatingConstraintChecker downgrades violations to warnings 
automatically based on the constraint parameters;
+        * constraint checkers should not assign this status directly.
+        */
+       const STATUS_WARNING = 'warning';
 
        /**
         * @var EntityId
@@ -164,6 +171,13 @@
        }
 
        /**
+        * @param string $status
+        */
+       public function setStatus( $status ) {
+               $this->status = $status;
+       }
+
+       /**
         * @return string (sanitized HTML)
         */
        public function getMessage() {
diff --git a/modules/SpecialConstraintReportPage.css 
b/modules/SpecialConstraintReportPage.css
index 7dcc63d..deb2ff2 100644
--- a/modules/SpecialConstraintReportPage.css
+++ b/modules/SpecialConstraintReportPage.css
@@ -25,7 +25,7 @@
 }
 
 .wbqc-status-exception {
-       color: #ac6600; /* Yellow30 */
+       color: #14866d; /* Green30 */
 }
 
 .wbqc-status-violation {
@@ -44,6 +44,10 @@
        color: #72777d; /* Base30 */
 }
 
+.wbqc-status-warning {
+       color: #ac6600; /* Yellow30 */
+}
+
 /* Tooltip */
 .wbqc-indicator {
        color: #72777d; /* Base30 */
diff --git a/modules/gadget.js b/modules/gadget.js
index d10b811..e3e3742 100644
--- a/modules/gadget.js
+++ b/modules/gadget.js
@@ -84,6 +84,10 @@
                                                label: mw.message( 
'wbqc-potentialissues-short' ).text()
                                        },
                                        {
+                                               status: 'warning',
+                                               label: mw.message( 
'wbqc-potentialissues-short' ).text()
+                                       },
+                                       {
                                                status: 'bad-parameters',
                                                label: mw.message( 
'wbqc-parameterissues-short' ).text(),
                                                subheading: mw.message( 
'wbqc-parameterissues-long' ).text(),
diff --git a/tests/phpunit/Api/CheckConstraintsTest.php 
b/tests/phpunit/Api/CheckConstraintsTest.php
index d30d45a..5d1e399 100644
--- a/tests/phpunit/Api/CheckConstraintsTest.php
+++ b/tests/phpunit/Api/CheckConstraintsTest.php
@@ -102,6 +102,7 @@
                        $config = new HashConfig( [
                                'WBQualityConstraintsPropertyConstraintId' => 
'P1',
                                'WBQualityConstraintsExceptionToConstraintId' 
=> 'P2',
+                               'WBQualityConstraintsConstraintStatusId' => 
'P3',
                        ] );
                        $entityIdParser = new ItemIdParser();
                        $constraintParameterRenderer = new 
ConstraintParameterRenderer( $entityIdFormatter, $valueFormatter );
@@ -233,7 +234,7 @@
                        'some guid',
                        $propertyId,
                        'violationConstraint',
-                       []
+                       [ 'constraint_status' => 'mandatory' ]
                );
        }
 
diff --git a/tests/phpunit/DelegatingConstraintCheckerTest.php 
b/tests/phpunit/DelegatingConstraintCheckerTest.php
index 0347b4e..aeb8efa 100644
--- a/tests/phpunit/DelegatingConstraintCheckerTest.php
+++ b/tests/phpunit/DelegatingConstraintCheckerTest.php
@@ -8,6 +8,8 @@
 use Wikibase\DataModel\Services\Lookup\EntityRetrievingDataTypeLookup;
 use Wikibase\DataModel\Services\Statement\StatementGuidParser;
 use Wikibase\Rdf\RdfVocabulary;
+use Wikibase\Repo\Tests\NewItem;
+use Wikibase\Repo\Tests\NewStatement;
 use 
WikibaseQuality\ConstraintReport\ConstraintCheck\DelegatingConstraintChecker;
 use WikibaseQuality\ConstraintReport\ConstraintReportFactory;
 use WikibaseQuality\ConstraintReport\Tests\ConstraintParameters;
@@ -262,7 +264,22 @@
                                        'pid' => 3,
                                        'constraint_type_qid' => 'Is not 
inside',
                                        'constraint_parameters' => '{}'
-                               ]
+                               ],
+                               [
+                                       'constraint_guid' => 
'P6$ad792000-6a12-413d-9fe5-11d2467b7a92',
+                                       'pid' => 6,
+                                       'constraint_type_qid' => 'Qualifier',
+                                       'constraint_parameters' => json_encode(
+                                               [
+                                                       'constraint_status' => 
'mandatory'
+                                               ] )
+                               ],
+                               [
+                                       'constraint_guid' => 
'P7$a3f746e7-66a0-46fd-96ab-6ff6638332a4',
+                                       'pid' => 7,
+                                       'constraint_type_qid' => 'Qualifier',
+                                       'constraint_parameters' => '{}'
+                               ],
                        ]
                );
        }
@@ -303,6 +320,22 @@
                $this->assertEquals( 'exception', $result[ 0 ]->getStatus(), 
'Should be an exception' );
        }
 
+       public function testCheckAgainstConstraintsWithMandatoryConstraint() {
+               $entity = NewItem::withId( 'Q6' )
+                       ->andStatement( NewStatement::noValueFor( 'P6' ) )
+                       ->build();
+               $result = $this->constraintChecker->checkAgainstConstraints( 
$entity );
+               $this->assertEquals( 'violation', $result[ 0 ]->getStatus(), 
'Should be a violation' );
+       }
+
+       public function testCheckAgainstConstraintsWithNonMandatoryConstraint() 
{
+               $entity = NewItem::withId( 'Q7' )
+                       ->andStatement( NewStatement::noValueFor( 'P7' ) )
+                       ->build();
+               $result = $this->constraintChecker->checkAgainstConstraints( 
$entity );
+               $this->assertEquals( 'warning', $result[ 0 ]->getStatus(), 
'Should be a warning' );
+       }
+
        public function testCheckAgainstConstraints_ByClaims() {
                $result = 
$this->constraintChecker->checkAgainstConstraintsOnClaimId(
                        'Q1$c0f25a6f-9e33-41c8-be34-c86a730ff30b' );
diff --git a/tests/phpunit/Helper/ConstraintParameterParserTest.php 
b/tests/phpunit/Helper/ConstraintParameterParserTest.php
index 6105589..e0e77f6 100644
--- a/tests/phpunit/Helper/ConstraintParameterParserTest.php
+++ b/tests/phpunit/Helper/ConstraintParameterParserTest.php
@@ -898,4 +898,51 @@
                );
        }
 
+       public function testParseConstraintStatusParameter() {
+               $constraintStatusId = $this->getDefaultConfig()->get( 
'WBQualityConstraintsConstraintStatusId' );
+               $mandatoryId = $this->getDefaultConfig()->get( 
'WBQualityConstraintsMandatoryConstraintId' );
+               $snak = new PropertyValueSnak( new PropertyId( 
$constraintStatusId ), new EntityIdValue( new ItemId( $mandatoryId ) ) );
+
+               $parsed = 
$this->getConstraintParameterParser()->parseConstraintStatusParameter(
+                       [ $constraintStatusId => [ 
$this->snakSerializer->serialize( $snak ) ] ]
+               );
+
+               $this->assertEquals( 'mandatory', $parsed );
+       }
+
+       public function testParseConstraintStatusParameterMissing() {
+               $parsed = 
$this->getConstraintParameterParser()->parseConstraintStatusParameter(
+                       []
+               );
+
+               $this->assertNull( $parsed );
+       }
+
+       public function testParseConstraintStatusParameterFromTemplate() {
+               $parsed = 
$this->getConstraintParameterParser()->parseConstraintStatusParameter(
+                       [ 'constraint_status' => 'mandatory' ]
+               );
+
+               $this->assertEquals( 'mandatory', $parsed );
+       }
+
+       public function testParseConstraintStatusParameterInvalid() {
+               $constraintStatusId = $this->getDefaultConfig()->get( 
'WBQualityConstraintsConstraintStatusId' );
+               $snak = new PropertyValueSnak( new PropertyId( 
$constraintStatusId ), new EntityIdValue( new ItemId( 'Q1' ) ) );
+
+               $this->assertThrowsConstraintParameterException(
+                       'parseConstraintStatusParameter',
+                       [ [ $constraintStatusId => [ 
$this->snakSerializer->serialize( $snak ) ] ] ],
+                       'wbqc-violation-message-parameter-oneof'
+               );
+       }
+
+       public function testParseConstraintStatusParameterFromTemplateInvalid() 
{
+               $this->assertThrowsConstraintParameterException(
+                       'parseConstraintStatusParameter',
+                       [ [ 'constraint_status' => 'other' ] ],
+                       'wbqc-violation-message-parameter-oneof'
+               );
+       }
+
 }
diff --git a/tests/phpunit/Specials/SpecialConstraintReportTest.php 
b/tests/phpunit/Specials/SpecialConstraintReportTest.php
index 49e74f1..4b793bd 100644
--- a/tests/phpunit/Specials/SpecialConstraintReportTest.php
+++ b/tests/phpunit/Specials/SpecialConstraintReportTest.php
@@ -127,13 +127,13 @@
                                        'constraint_guid' => '1',
                                        'pid' => self::$idMap[ 'P1' 
]->getNumericId(),
                                        'constraint_type_qid' => 'Multi value',
-                                       'constraint_parameters' => '{}'
+                                       'constraint_parameters' => 
'{"constraint_status":"mandatory"}'
                                ],
                                [
                                        'constraint_guid' => '3',
                                        'pid' => self::$idMap[ 'P1' 
]->getNumericId(),
                                        'constraint_type_qid' => 'Single value',
-                                       'constraint_parameters' => '{}'
+                                       'constraint_parameters' => 
'{"constraint_status":"mandatory"}'
                                ]
                        ]
                );

-- 
To view, visit https://gerrit.wikimedia.org/r/366582
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6fa0af86dd7d3d00abc4cd004417667028599428
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/WikibaseQualityConstraints
Gerrit-Branch: master
Gerrit-Owner: Lucas Werkmeister (WMDE) <lucas.werkmeis...@wikimedia.de>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to