Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add recommendation module for LibGuides Profiles #2977

Merged
merged 30 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ac48fbf
LibGuides Profile recommendation module
maccabeelevine Jul 3, 2023
413369c
Add email and escape output
maccabeelevine Jul 5, 2023
b39a416
Refactor oauth connection logic
maccabeelevine Jul 5, 2023
9730793
Add documention and clean code
maccabeelevine Jul 5, 2023
8e0af3c
Remove comment
maccabeelevine Jul 5, 2023
ff8e614
Fix log message
maccabeelevine Jul 5, 2023
9e0983f
Document in searches.ini
maccabeelevine Jul 5, 2023
c746cf2
Clarify API documentation link
maccabeelevine Jul 6, 2023
2e9766c
Note CSP requirement
maccabeelevine Jul 6, 2023
2b2baac
Add class property for user agent.
maccabeelevine Jul 6, 2023
6f5d0d6
Simplify if/else condition
maccabeelevine Jul 6, 2023
a9008c5
Document $tokenData class
maccabeelevine Jul 6, 2023
80f1ba3
Fix comments & spacing
maccabeelevine Jul 6, 2023
5b042c6
Remove unused method
maccabeelevine Jul 6, 2023
b865a03
Fix indent
maccabeelevine Jul 6, 2023
b30edc8
Translate heading and fix class names
maccabeelevine Jul 6, 2023
ec97543
Check for empty values
maccabeelevine Jul 6, 2023
8da260b
Handle empty last name.
maccabeelevine Jul 6, 2023
840e62e
Add missing heading
maccabeelevine Jul 6, 2023
7c1ee61
Remove unused $forceNewConnection
maccabeelevine Jul 6, 2023
cc38660
Return null if failure in getAccounts()
maccabeelevine Jul 6, 2023
e80a06e
Create LibGuides connector via factory
maccabeelevine Jul 6, 2023
7f9a5ab
Log only if $this->log exists
maccabeelevine Jul 6, 2023
758e088
Avoid collsions with safe logging
maccabeelevine Jul 6, 2023
71c2ab1
Cache /accounts response
maccabeelevine Jul 6, 2023
31ea155
Fix mention of LoggerAwareInterface
maccabeelevine Jul 6, 2023
78c653f
Remove useless algorithm
maccabeelevine Jul 6, 2023
15ddd33
Fix typo
maccabeelevine Jul 7, 2023
755d8bc
Rename methods for clarity
maccabeelevine Jul 7, 2023
6e261f9
Fix typo
maccabeelevine Jul 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions config/vufind/LibGuidesAPI.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
; This file is used for the LibGuides *API*, which uses an
; oauth connection mechanism. This API is currently used
; by the LibGuidesProfile recommendation module.

; LibGuides API documentation:
; https://ask.springshare.com/libguides/faq/873
demiankatz marked this conversation as resolved.
Show resolved Hide resolved

; This file is NOT used by the LibGuides (research guides)
; or LibGuidesAZ (database list) data sources, which
; do not actually use the LibGuides API
; -- they retrieve data through a LibGuides Search Box widget.
; https://ask.springshare.com/libguides/faq/867
; See LibGuides.ini and LibGuidesAZ.ini for that configuration.

[General]
; Client Id & Secret provided by LibGuides API configuration
; See https://ask.springshare.com/libguides/faq/873#api-auth
;client_id = 123
;client_secret = ABCD

; Base URL of the LibGuides API
api_base_url = https://lgapi-us.libapps.com/1.2

; Calls to GET (...)/accounts endpoint
[GetAccounts]
; Duration (seconds) to cache response data. Default is 600.
;cache_lifetime = 600
2 changes: 2 additions & 0 deletions config/vufind/contentsecuritypolicy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ worker-src[] = "blob:"
style-src[] = "'self'"
style-src[] = "'unsafe-inline'"
img-src[] = "'self'"
; If you are using LibGuidesProfile recommendation module, uncomment the line below
;img-src[] = libapps.s3.amazonaws.com
demiankatz marked this conversation as resolved.
Show resolved Hide resolved
; If you are using MapSelection recommendation module, uncomment a line below
; for the basemap you are using:
;img-src[] = "https://maps.wikimedia.org"
Expand Down
5 changes: 5 additions & 0 deletions config/vufind/searches.ini
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ WorkKeys = year
; FacetCloud:[ini section]:[ini name]
; Same functionality as ExpandFacets, but with a more compact interface to
; allow the display of more values.
; LibGuidesProfile
; Display a single LibGuides Profile for a LibGuides user (likely a librarian)
; who owns a subject guide whose title most closely matches the search terms.
; Uses configuration in LibGuidesAPI.ini and requires configuration in
; contentsecuritypolicy.ini.
; LibGuidesResults:[GET parameter]:[result limit]
; Display LibGuides research guides search results matching the terms found in
; the specified GET parameter (default = "lookfor"), limited to a specified
Expand Down
1 change: 1 addition & 0 deletions languages/en.ini
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ Last Name = "Last Name"
Last Search Result = "Last Search Result"
less = "less"
less_ellipsis = "less…"
libguides_profile_suggestion_heading = "Talk to a Subject Librarian"
libguides_recommendations = "Research Guide Results"
libguides_recommendations_more = "More Research Guide Results"
libguidesaz_recommendations = "Database Results"
Expand Down
1 change: 1 addition & 0 deletions module/VuFind/config/module.config.php
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@
'VuFind\Config\PluginManager' => 'VuFind\Config\PluginManagerFactory',
'VuFind\Config\SearchSpecsReader' => 'VuFind\Config\YamlReaderFactory',
'VuFind\Config\YamlReader' => 'VuFind\Config\YamlReaderFactory',
'VuFind\Connection\LibGuides' => 'VuFind\Connection\LibGuidesFactory',
'VuFind\Connection\Relais' => 'VuFind\Connection\RelaisFactory',
'VuFind\Connection\WorldCatUtils' => 'VuFind\Connection\WorldCatUtilsFactory',
'VuFind\Content\PageLocator' => 'VuFind\Content\PageLocatorFactory',
Expand Down
190 changes: 190 additions & 0 deletions module/VuFind/src/VuFind/Connection/LibGuides.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<?php

/**
* LibGuides API connection class.
*
* PHP version 8
*
* Copyright (C) Villanova University 2023.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Connection
* @author Demian Katz <demian.katz@villanova.edu>
* @author Brent Palmer <brent-palmer@icpl.org>
* @author Maccabee Levine <msl321@lehigh.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org
*/

namespace VuFind\Connection;

use Exception;
use Laminas\Log\LoggerAwareInterface;

/**
* LibGuides API connection class.
*
* Note: This is for the LibGuides API used by the LibGuidesProfile recommendation service,
* this is *not* for the LibGuides search widget "API" used by the LibGuides and LibGuidesAZ
* data sources.
*
* Closely adapted from VuFind\DigitalContent\OverdriveConnector.
*
* @category VuFind
* @package Connection
* @author Demian Katz <demian.katz@villanova.edu>
* @author Brent Palmer <brent-palmer@icpl.org>
* @author Maccabee Levine <msl321@lehigh.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org
*/
class LibGuides implements
OauthServiceInterface,
\VuFindHttp\HttpServiceAwareInterface,
LoggerAwareInterface
{
use OauthServiceTrait;
use \VuFindHttp\HttpServiceAwareTrait;
use \VuFind\Log\LoggerAwareTrait {
logError as error;
}

/**
* HTTP Client
*
* @var \Laminas\Http\HttpClient
*/
protected $client;

/**
* Base URL of the LibGuides API
*
* @var string
*/
protected $baseUrl;

/**
* Client ID for a client_credentials grant
*
* @var string
*/
protected $clientId;

/**
* Client Secret for a client_credentials grant
*
* @var string
*/
protected $clientSecret;

/**
* User agent to send in header
*
* @var string
*/
protected $userAgent = "VuFind";

/**
* Constructor
*
* @param Config $config LibGuides API configuration object
* @param \Laminas\Http\Client $client HTTP client
*
* @link https://ask.springshare.com/libguides/faq/873#api-auth
*/
public function __construct(
$config,
$client
) {
$this->client = $client;
$this->baseUrl = $config->General->api_base_url;
$this->clientId = $config->General->client_id;
$this->clientSecret = $config->General->client_secret;
}

/**
* Load all LibGuides accounts.
*
* @return object|null A JSON object of all LibGuides accounts, or null
* if an error occurs
*/
public function getAccounts()
{
$tokenData = $this->authenticateWithClientCredentials(
$this->baseUrl . "/oauth/token",
$this->clientId,
$this->clientSecret
);
if (!$tokenData) {
return null;
}

$headers = [];
if (
isset($tokenData->token_type)
&& isset($tokenData->access_token)
) {
$headers[] = "Authorization: {$tokenData->token_type} "
. $tokenData->access_token;
}
$headers[] = "User-Agent: " . $this->userAgent;

$this->client->setHeaders($headers);
$this->client->setMethod("GET");
$this->client->setUri(
$this->baseUrl . "/accounts?expand=profile,subjects"
);
try {
$response = $this->client->send();
} catch (Exception $ex) {
$this->error(
"Exception during request: " .
$ex->getMessage()
);
return null;
}

if ($response->isServerError()) {
$this->error(
"LibGuides API HTTP Error: " .
$response->getStatusCode()
);
$this->debug("Request: " . $this->client->getRequest());
$this->debug("Response: " . $this->client->getResponse());
return null;
}
$body = $response->getBody();
$returnVal = json_decode($body);
$this->debug(
"Return from LibGuides API Call: " . print_r($returnVal, true)
);
if ($returnVal != null) {
if (isset($returnVal->errorCode)) {
// In some cases, this should be returned perhaps...
demiankatz marked this conversation as resolved.
Show resolved Hide resolved
$this->error("LibGuides Error: " . $returnVal->errorCode);
}
return $returnVal;
} else {
$this->error(
"LibGuides API Error: Nothing returned from API call."
);
$this->debug(
"Body return from LibGuides API Call: " . print_r($body, true)
);
}
return null;
}
}
87 changes: 87 additions & 0 deletions module/VuFind/src/VuFind/Connection/LibGuidesFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

/**
* Libuides API connection factory.
*
* PHP version 8
*
* Copyright (C) Villanova University 2023.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Connection
* @author Demian Katz <demian.katz@villanova.edu>
* @author Maccabee Levine <msl321@lehigh.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development Wiki
*/

namespace VuFind\Connection;

use Laminas\ServiceManager\Exception\ServiceNotCreatedException;
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
use Laminas\ServiceManager\Factory\FactoryInterface;
use Psr\Container\ContainerExceptionInterface as ContainerException;
use Psr\Container\ContainerInterface;

/**
* LibGuides API connection factory.
*
* @category VuFind
* @package Connection
* @author Demian Katz <demian.katz@villanova.edu>
* @author Maccabee Levine <msl321@lehigh.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development Wiki
*/
class LibGuidesFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container Service manager
* @param string $requestedName Service being created
* @param null|array $options Extra options (optional)
*
* @return object
*
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException&\Throwable if any other error occurs
*/
public function __invoke(
ContainerInterface $container,
$requestedName,
array $options = null
) {
if (!empty($options)) {
throw new \Exception('Unexpected options sent to factory.');
}
$config = $container->get(\VuFind\Config\PluginManager::class)
->get('LibGuidesAPI');
if (!isset($config->General->client_id)) {
throw new \Exception('client_id key missing from configuration.');
}
if (!isset($config->General->client_secret)) {
throw new \Exception('client_secret key missing from configuration.');
}
$client = $container->get(\VuFindHttp\HttpService::class)->createClient();
return new $requestedName(
$config,
$client
);
}
}