Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 75
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
MathMLIntents
0.00% covered (danger)
0.00%
0 / 75
0.00% covered (danger)
0.00%
0 / 5
420
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 run
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 getConceptIdMap
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 processRow
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
110
 getNumericPropertyId
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Extension\MathSearch\Graph\Job;
4
5use DataValues\QuantityValue;
6use DataValues\StringValue;
7use MediaWiki\Extension\MathSearch\Graph\Query;
8use Throwable;
9use Wikibase\DataModel\Entity\Item;
10use Wikibase\DataModel\Entity\ItemId;
11use Wikibase\DataModel\Entity\NumericPropertyId;
12use Wikibase\DataModel\Services\Lookup\EntityLookup;
13use Wikibase\DataModel\Services\Statement\GuidGenerator;
14use Wikibase\DataModel\Snak\PropertyValueSnak;
15use Wikibase\Lib\Store\EntityStore;
16use Wikibase\Repo\WikibaseRepo;
17
18class MathMLIntents extends GraphJob {
19    private EntityStore $entityStore;
20    private EntityLookup $entityLookup;
21    private GuidGenerator $guidGenerator;
22
23    private array $propertyIds = [];
24
25    public function __construct( $params ) {
26        parent::__construct( 'MathMLIntents', $params );
27        $this->entityStore = WikibaseRepo::getEntityStore();
28        $this->entityLookup = WikibaseRepo::getEntityLookup();
29        $this->guidGenerator = new GuidGenerator();
30    }
31
32    public function run(): bool {
33        $qIdMap = $this->getConceptIdMap();
34        foreach ( $this->params['rows'] as $concept => $row ) {
35            try {
36                if ( !isset( $qIdMap[(string)$concept] ) ) {
37                    self::getLog()->debug( "No Qid found for $concept." );
38                    $qid = false;
39                } else {
40                    $qid = $qIdMap[$concept];
41                }
42                $this->processRow( $concept, $qid, $row );
43            } catch ( Throwable $ex ) {
44                self::getLog()->error( "Skip processing $concept", [ 'error' => $ex ] );
45            }
46        }
47
48        return true;
49    }
50
51    private function getConceptIdMap(): array {
52        $concepts = '"' . implode( '" "', array_keys( $this->params['rows'] ) ) . '"';
53        $query = Query::getQidFromConcept( $concepts );
54        $rs = Query::getResults( $query );
55        $qIdMap = [];
56        foreach ( $rs as $row ) {
57            $de = $row['de'];
58            if ( isset( $qIdMap[$de] ) ) {
59                self::getLog()->error( "Multiple Qid found for Zbl $de." );
60                unset( $this->params['rows'][$de] );
61                continue;
62            }
63            $qIdMap[$de] = $row['qid'];
64        }
65
66        return $qIdMap;
67    }
68
69    private function processRow( $concept, $qid, $row ) {
70        global $wgMathIntentsQIdMap;
71        $pDe = $this->getNumericPropertyId( $wgMathIntentsQIdMap['concept'] );
72        self::getLog()->info( "Add OpenAlex data for Zbl $concept to $qid." );
73        if ( $qid ) {
74            $item = $this->entityLookup->getEntity( ItemId::newFromNumber( $qid ) );
75            if ( !$item instanceof Item ) {
76                self::getLog()->error( "Item Q$qid not found." );
77                return;
78            }
79            $statements = $item->getStatements();
80            $profileTypeStatements = $statements->getByPropertyId( $pDe )->getMainSnaks();
81            if ( count( $profileTypeStatements ) !== 1 ) {
82                self::getLog()->error( "No or multiple statements found for concept $concept." );
83                return;
84            }
85            $mainSnak = $profileTypeStatements[0];
86            if ( !$mainSnak->getDataValue()->getValue() == $concept ) {
87                self::getLog()->error( "Wrong statement found for concept $concept." );
88                return;
89            }
90            $changed = false;
91        } else {
92            $item = new Item();
93            $this->entityStore->assignFreshId( $item );
94            $statements = $item->getStatements();
95            $changed = true;
96        }
97        foreach ( $row['content'] as $k => $value ) {
98            if ( !array_key_exists( $k, $wgMathIntentsQIdMap ) ) {
99                continue;
100            }
101            $key = $wgMathIntentsQIdMap[$k];
102            $propertyId = $this->getNumericPropertyId( $key );
103            $profileTypeStatements = $statements->getByPropertyId( $propertyId );
104            if ( !$profileTypeStatements->isEmpty() ) {
105                self::getLog()->debug( "$key of concept $concept already set." );
106                continue;
107            }
108
109            $changed = true;
110
111            $mainSnak = new PropertyValueSnak(
112                $propertyId,
113                $key === $wgMathIntentsQIdMap['arity'] ?
114                    QuantityValue::newFromNumber( $value )
115                    : new StringValue( $value )
116            );
117            $statements->addNewStatement( $mainSnak, [], null,
118                $this->guidGenerator->newGuid( $item->getId() ) );
119        }
120        if ( $changed === false ) {
121            self::getLog()->info( "Skip Zbl $concept  (no change)." );
122            return;
123        }
124        $item->setStatements( $statements );
125        $this->entityStore->saveEntity( $item, "Set OpenAlex properties.", $this->getUser(),
126            EDIT_FORCE_BOT );
127    }
128
129    public function getNumericPropertyId( string $key ): NumericPropertyId {
130        if ( !isset( $this->propertyIds[$key] ) ) {
131            $this->propertyIds[$key] = new NumericPropertyId( $key );
132        }
133        return $this->propertyIds[$key];
134    }
135}