Nilesh has submitted this change and it was merged. Change subject: Added prototype PHP client ......................................................................
Added prototype PHP client Change-Id: Iefd1996468a684be57aabde18faffa114b08f9df --- A php-client/README.md A php-client/composer.json A php-client/src/WesPHPClient/EntitySuggesterService.php A php-client/src/WesPHPClient/MyrrixClient.php A php-client/src/WesPHPClient/service.json A php-client/wesTest.php 6 files changed, 474 insertions(+), 0 deletions(-) Approvals: Nilesh: Verified; Looks good to me, approved diff --git a/php-client/README.md b/php-client/README.md new file mode 100644 index 0000000..f3b0a2b --- /dev/null +++ b/php-client/README.md @@ -0,0 +1,79 @@ +# Wikidata Entity Suggester PHP Client + +This is the PHP client for the [Wikidata Entity Suggester](https://github.com/nilesh-c/wikidata-entity-suggester). It uses the Entity Suggester's REST API to push data and get suggestions. + +## Installation via Composer + +The best way to use the library is via [Composer](http://getcomposer.org/). + +After you install composer, run in console: + +``` +cd your/working/directory +composer require guzzle/guzzle +composer require wes-php-client/wes-php-client +``` + +Type 'dev-master' for both, when prompted for the version. + +OR + +You can manually add the library to your dependencies in the composer.json file: + +``` +{ + "require": { + "wes-php-client/wes-php-client": "dev-master" + } +} +``` + +and install your dependencies: + +``` +composer install +``` + +## Usage + +``` php +// Always include this file to use the client +require_once("vendor/autoload.php"); + +// Instanciate the Myrrix/Entity Suggester service +$wes = new EntitySuggesterService('localhost', 8080); + +// Push the data in the file /path/data.csv into the Entity Suggester. +// Please check [this page](https://github.com/nilesh-c/wikidata-entity-suggester/wiki/CSV-file-explanation) for info on how the data should be structured in the CSV file. +$wes->ingestFile("/path/data.csv"); + +// Refresh the index (add newly added data into the model) +$wes->refresh(); + +// Get value recommendations for a new item +$recommendation = $myrrix->getRecommendation(array( + "107----4167410", + "106", + "107----215627", + "156" + ), "value"); // returns an array of property-value pairs and strengths (example: [["107----4167410",0.53],["373----Huntsville Alabama",0.499]]) + +// Get property recommendations for a new item +$recommendation = $myrrix->getRecommendation(array( + "107----4167410", + "106", + "107----215627", + "156" + ), "property"); // returns an array of properties and strengths (example: [["25",0.53],["156",0.499]]) + +// Specify the number of recommendations (optional) +$recommendation = $myrrix->getRecommendation(array( + "107----4167410", + "106", + "107----215627", + "156" + ), "property", 20); // returns an array of 20 properties with strengths (example: [["25",0.53],["156",0.499]]) + +``` + +See [wesTest.php](wesTest.php) for a crude example/demo. It is temporarily deployed [here](http://173.0.50.123/wesTest.php). diff --git a/php-client/composer.json b/php-client/composer.json new file mode 100644 index 0000000..606cad1 --- /dev/null +++ b/php-client/composer.json @@ -0,0 +1,23 @@ +{ + "name": "wes-php-client/wes-php-client", + "description": "PHP Client for Wikidata Entity Suggester", + "type": "library", + "keywords": ["wikidata", "entity-suggester", "myrrix", "recommendation"], + "homepage": "https://github.com/nilesh-c/wes-php-client", + "license": "MIT", + "stability": "dev", + "minimum-stability": "dev", + "autoload": { + "psr-0": { "WesPHPClient": "src/" } + }, + "require": { + "guzzle/guzzle": "dev-master" + }, + "authors": [ + { + "name": "Nilesh Chakraborty", + "email": "[email protected]", + "homepage": "http://nileshc.com" + } + ] +} diff --git a/php-client/src/WesPHPClient/EntitySuggesterService.php b/php-client/src/WesPHPClient/EntitySuggesterService.php new file mode 100644 index 0000000..c098237 --- /dev/null +++ b/php-client/src/WesPHPClient/EntitySuggesterService.php @@ -0,0 +1,120 @@ +<?php + +namespace WesPHPClient; + +/** + * EntitySuggesterService helps you leverage the Entity Suggester REST api + */ +class EntitySuggesterService { + + /** + * @var MyrrixClient + */ + protected $client; + + /** + * @param string $host The hostname + * @param int $port The port + * @param string $username The username + * @param string $password The password + */ + function __construct($host, $port, $username = null, $password = null) { + $this->client = MyrrixClient::factory(array( + 'hostname' => $host, + 'port' => $port, + 'username' => $username, + 'password' => $password, + )); + } + + /** + * Gets a recommendation for an unknown item, infer its recommended properties/values using a preference(list of properties and property-values) array. + * + * @param array $properties The known properties of the unknown item + * @param int $count The number of results to retrieve + * + * @return array + */ + public function getRecommendation(array $properties = array(), $type = "property", $count = null) { + $command = $this->client->getCommand('GetRecommendation', array( + 'properties' => $properties, + 'type'=>$type, + 'howMany' => $count, + )); + + return $this->client->execute($command)->json(); + } + + /** + * Sets a list of item-property or item-property+value pairs + * + * @param array $properties An array of arrays with keys 'userID', 'itemID' and 'value' + * + * @return bool + */ + public function ingest(array $properties) { + $command = $this->client->getCommand('Ingest', array( + 'data' => $properties, + )); + + return $this->client->execute($command)->isSuccessful(); + } + + /** + * Sets a list of item-property or item-property+value pairs from a CSV file + * + * @param string $fileName The path/filename of the csv file with <item>,<property/value>,<strength value> entries + * + * @return bool + */ + public function ingestFile(string $fileName) { + $f = fopen($fileName, "r"); + $ingestFeed = array(); + if($f === false) + throw new Exception("Sorry, cannot open file $fileName"); + while(!feof($f)) { + $prefs = trim(fgets($f)); + if(empty($prefs)) { + continue; + } else { + $prefs = explode(",", $prefs); + } + $ingestFeed[] = array( + 'userID'=>$prefs[0], + 'itemID'=>$prefs[1], + 'value'=>$prefs[2] + ); + } + return $this->ingest($ingestFeed); + } + + /** + * Asks Myrrix to refresh, may take time. + * + * @return bool + */ + public function refresh() { + $command = $this->client->getCommand('Refresh'); + + return $this->client->execute($command)->isSuccessful(); + } + + /** + * Asks if Myrrix is ready to answer requests. + * + * @return bool + */ + public function isReady() { + $command = $this->client->getCommand('Ready'); + + return $this->client->execute($command)->isSuccessful(); + } + + /** + * @return MyrrixClient + */ + public function getClient() { + return $this->client; + } + +} diff --git a/php-client/src/WesPHPClient/MyrrixClient.php b/php-client/src/WesPHPClient/MyrrixClient.php new file mode 100644 index 0000000..1f4cc4b --- /dev/null +++ b/php-client/src/WesPHPClient/MyrrixClient.php @@ -0,0 +1,47 @@ +<?php + +namespace WesPHPClient; + +use Guzzle\Service\Client; +use Guzzle\Common\Collection; +use Guzzle\Service\Description\ServiceDescription; +use Guzzle\Parser\ParserRegistry; +use Guzzle\Plugin\CurlAuth\CurlAuthPlugin; + +class MyrrixClient extends Client { + + public static function factory($config = array()) { + $default = array( + 'base_url' => 'http://{hostname}:{port}', + 'hostname' => 'localhost', + 'port' => 8080, + 'username' => null, + 'password' => null, + ); + $required = array('hostname', 'port', 'base_url', 'username', 'password'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self($config->get('base_url'), $config); + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'service.json')); + + $client->setDefaultHeaders(array( + 'Accept' => 'text/html', + )); + + $authPlugin = new CurlAuthPlugin($config['username'], $config['password']); + $client->addSubscriber($authPlugin); + + return $client; + } + + public static function filterIngestData(array $data) { + $result = ''; + + foreach ($data as $line) { + $result .= $line['userID'] . ',' . $line['itemID'] . (isset($line['value']) ? ',' . $line['value'] : '') . PHP_EOL; + } + + return $result; + } + +} diff --git a/php-client/src/WesPHPClient/service.json b/php-client/src/WesPHPClient/service.json new file mode 100644 index 0000000..6556991 --- /dev/null +++ b/php-client/src/WesPHPClient/service.json @@ -0,0 +1,79 @@ +{ + "name": "Wikidata Entity Suggester", + "apiVersion": "2012-12-15", + "description": "Wikidata Entity Suggester is a recommendation engine for wikidata that can suggest properties or values to a newly created or not yet created item", + "operations": { + "GetRecommendation": { + "httpMethod": "GET", + "uri": "/entitysuggester/suggest{/properties*}?type={type}", + "summary": "Gets a recommendation for an anonymous item", + "parameters": { + "properties": { + "location": "uri", + "description": "The properties and property-value pairs that the item has. Keys are the items, values are the strengths of the associations.", + "required": true, + "type": "array", + "items": { + "description": "A value describing the strength of the observed association.", + "type": "string" + } + }, + "type": { + "location": "uri", + "description": "Type of recommendation - property or value.", + "required": true, + "type": "string" + }, + "howMany": { + "location": "query", + "description": "Maximum number of recommendations to return.", + "type": "integer" + } + } + }, + "Ingest": { + "httpMethod": "POST", + "uri": "/entitysuggester/ingest", + "summary": "Supports bulk-loading new properties.", + "parameters": { + "data": { + "description": "New properties", + "required": true, + "location": "body", + "type": "array", + "minItems": 1, + "filters": ["WesPHPClient\\MyrrixClient::filterIngestData"], + "items": { + "type": "object", + "properties": { + "userID": { + "type": "string", + "description": "The user ID", + "required": true + }, + "itemID": { + "type": "string", + "description": "The item ID", + "required": true + }, + "value": { + "description": "A value describing the strength of the observed association.", + "type": "numeric" + } + } + } + } + } + }, + "Ready": { + "httpMethod": "HEAD", + "uri": "/ready", + "summary": "Tells whether the Serving Layer is ready to answer requests -- has loaded or computed a model." + }, + "Refresh": { + "httpMethod": "POST", + "uri": "/refresh", + "summary": "Requests that the recommender rebuild its internal state and models." + } + } +} diff --git a/php-client/wesTest.php b/php-client/wesTest.php new file mode 100644 index 0000000..6f74ff6 --- /dev/null +++ b/php-client/wesTest.php @@ -0,0 +1,126 @@ +<!DOCTYPE html> +<html> + <head> + <title>Testing PHP client for Wikidata Entity Suggester</title> + </head> + <body> +<?php +require_once("vendor/autoload.php"); + +use WesPHPClient\EntitySuggesterService; + +if (!isset($_POST['data'])) { + ?> + <h1>Enter data</h1> + <form action="wesTest.php" method="post"> + Please enter the properties and property----value pairs in this text box. An example set is already given.<br/><br/> + <textarea rows="10" cols="50" name="data"> +107----4167410 +106 +107----215627 +156 + </textarea><br/><br/> + <input type="radio" name="t" value="value" checked>Suggest values + <input type="radio" name="t" value="property">Suggest properties + <br/><br/> + <input type="submit" value="Get suggestions!"/> + </form> + + <?php + exit; +} +$wes = new EntitySuggesterService('127.0.0.1', 8080); +$type = isset($_POST['t']) ? $_POST['t'] : "property"; +if (trim($_POST['data']) == '') + die("You must provide some data."); +$data = array_map('trim', explode("\n", str_replace(" ", "", $_POST['data']))); + +$results = $wes->getRecommendation($data, $type, 10); + +mysql_connect("localhost", "root", "hackalot"); +mysql_select_db("wikidatawiki") or die(mysql_error()); + +if ($type == "property") { + echo "<h1>Suggested properties:</h1><br/>\n"; + $query = "SELECT pl_id, pl_text FROM plabel WHERE pl_lang='en' AND pl_id IN ("; + ?> + <table border="1"> + <tr> + <th>PropertyID</th> + <th>Property Value</th> + </tr> + <?php + foreach ($results as $result) { + $query .= "$result[0],"; + } + $query = substr($query, 0, -1) . ")"; + $mysqlResult = mysql_query($query) or die(mysql_error()); + while ($fetch = mysql_fetch_assoc($mysqlResult)) { + ?> + <tr> + <td><?php echo $fetch['pl_id']; ?></td> + <td><?php echo $fetch['pl_text']; ?></td> + </tr> + <?php + } +} else { + echo "<h1>Suggested property-value pairs:</h1><br/>\n"; + ?> + <table border="1"> + <tr> + <th>PropertyID</th> + <th>Property</th> + <th>ValueID</th> + <th>Value</th> + </tr> + <?php + $map = array(); + $query = "SELECT pl_id, pl_text FROM plabel WHERE pl_lang='en' AND pl_id IN ("; + foreach ($results as $result) { + $result = explode("----", $result[0]); + $query .= $result[0] . ","; + } + $query = substr($query, 0, -1) . ")"; + $mysqlResult = mysql_query($query) or die(mysql_error()); + while ($fetch = mysql_fetch_assoc($mysqlResult)) { + $map[$fetch['pl_id']] = $fetch['pl_text']; + } + + $query = "SELECT l_id, l_text FROM label WHERE l_lang='en' AND l_id="; + foreach ($results as $result) { + $result = explode("----", $result[0]); + if (!isset($result[1])) { + continue; + } else { + if (is_numeric($result[1])) { + $mysqlResult = mysql_query($query . $result[1]) or die(mysql_error()); + while ($fetch = mysql_fetch_assoc($mysqlResult)) { + $valID = $fetch['l_id']; + $valText = $fetch['l_text']; + } + ?> + <tr> + <td><?php echo $result[0]; ?></td> + <td><?php echo $map[$result[0]]; ?></td> + <td><?php echo $valID; ?></td> + <td><?php echo $valText; ?></td> + </tr> + <?php + } else { + ?> + <tr> + <td><?php echo $result[0]; ?></td> + <td><?php echo $map[$result[0]]; ?></td> + <td></td> + <td><?php echo $result[1]; ?></td> + </tr> + <?php + } + } + } +} +flush(); +?> + <a href="wesTest.php"> Go Back </a> + </body> + </html> \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/72116 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Iefd1996468a684be57aabde18faffa114b08f9df Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/WikidataEntitySuggester Gerrit-Branch: master Gerrit-Owner: Nilesh <[email protected]> Gerrit-Reviewer: Nilesh <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
