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

[FINNA-2838] Add Finna specific OAI metadata format #3097

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions local/config/finna/SearchApiRecordFields.yaml.sample
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ corporateAuthors:
type: array
items:
type: string
ctrlnum:
vufind.method: getCtrlNum
description: Record control numbers
type: array
items:
type: string
dedupIds:
vufind.method: "Formatter::getDedupIds"
description: IDs of all records deduplicated with the current record
Expand Down Expand Up @@ -337,6 +343,12 @@ lccn:
type: array
items:
type: string
majorGenres:
vufind.method: getMajorGenres
description: Major genres
type: array
items:
type: string
manufacturer:
vufind.method: getManufacturer
description: manufacturer
Expand All @@ -347,6 +359,12 @@ measurements:
type: array
items:
type: string
mediaTypes:
vufind.method: getMediaTypesAsStrings
description: Media types collected from links
type: array
items:
type: string
newerTitles:
vufind.method: getNewerTitles
description: Successor titles
Expand Down Expand Up @@ -476,6 +494,10 @@ rawData:
vufind.method: "Formatter::getRawData"
description: All data in the index fields
type: string
recordFormat:
vufind.method: getRecordFormat
description: Record format
type: string
recordLinks:
vufind.method: "Formatter::getRecordLinks"
description: Links to other related records
Expand Down Expand Up @@ -618,6 +640,20 @@ urls:
type: array
items:
$ref: '#/components/schemas/Url'
usageRights:
vufind.method: getUsageRights
description: Usage rights
(see [usage_rights_str_mv at https://www.kiwi.fi/display/Finna/Kenttien+mappaukset+eri+formaateista+Finnan+indeksiin]
(https://github.com/NatLibFi/RecordManager-Finna/blob/dev/mappings/usage_rights.map.sample))
type: array
items:
type: string
usageRightsExtended:
vufind.method: getUsageRightsExt
description: Extended usage rights (see https://github.com/NatLibFi/RecordManager-Finna/blob/dev/mappings/usage_rights_ext.map.sample)
type: array
items:
$ref: '#/components/schemas/TranslatedField'
year:
vufind.method: getYear
description: >
Expand Down
1 change: 1 addition & 0 deletions local/config/finna/config.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,7 @@ url = https://www.myendnoteweb.com/EndNoteWeb.html
;set_query['eod_books'] = "institution:kfu AND publishDate:[1911 TO 1911]"
;set_query['eod_ebooks'] = "format:eBook"
;vufind_api_format_fields = "id,authors,cleanIsbn,cleanIssn,formats,title"
;finna_api_format_fields = "id,authors,cleanIsbn,cleanIssn,formats,title,images,mediaTypes,ctrlnum,institutions,year,recordFormat,sectors,majorGenres,usageRights,usageRightsExtended,measurements,collections"
EreMaijala marked this conversation as resolved.
Show resolved Hide resolved

; Proxy Server is Optional.
[Proxy]
Expand Down
148 changes: 148 additions & 0 deletions module/Finna/src/Finna/OAI/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

namespace Finna\OAI;

use VuFind\RecordDriver\AbstractBase as AbstractRecordDriver;

/**
* OAI Server class
*
Expand All @@ -42,6 +44,31 @@
*/
class Server extends \VuFind\OAI\Server
{
/**
* Finna specific api fields from [OAI] section
*
* @var array
*/
protected array $finnaApiFields = [];

/**
* Finna metadata prefix
*
* @var string
*/
protected const OAI_FINNA_JSON = 'oai_finna_json';

/**
* Does the current configuration support the Finna metadata format (using
* the API's record formatter.
*
* @return bool
*/
protected function supportsFinnaMetadata()
{
return !empty($this->finnaApiFields) && null !== $this->recordFormatter;
}

/**
* Initialize data about metadata formats. (This is called on demand and is
* defined as a separate method to allow easy override by child classes).
Expand Down Expand Up @@ -69,5 +96,126 @@ protected function initializeMetadataFormats()
$this->metadataFormats['oai_qdc'] = [
'schema' => $qdc,
'namespace' => 'urn:dc:qdc:container'];
if ($this->supportsFinnaMetadata()) {
$this->metadataFormats[self::OAI_FINNA_JSON] = [
'schema' => 'https://vufind.org/xsd/oai_vufind_json-1.0.xsd',
'namespace' => 'http://vufind.org/oai_vufind_json-1.0',
];
}
}

/**
* Get record as a metadata presentation
*
* @param AbstractRecordDriver $record A record driver object
* @param string $format Metadata format to obtain
*
* @return string|bool String on success or false if error occurs
*/
protected function getRecordAsXML(AbstractRecordDriver $record, string $format): string|false
{
if (self::OAI_FINNA_JSON === $format && $this->supportsFinnaMetadata()) {
return $this->getFinnaMetadata($record);
}
return parent::getRecordAsXml($record, $format);
}

/**
* Respond to a ListMetadataFormats request.
*
* @return string|false
*/
protected function listMetadataFormats()
{
// If a specific ID was provided, try to load the related record; otherwise,
// set $record to false so we know it is a generic request.
if (isset($this->params['identifier'])) {
if (!($record = $this->loadRecord($this->params['identifier']))) {
return $this->showError('idDoesNotExist', 'Unknown Record');
}
} else {
$record = false;
}

// Loop through all available metadata formats and see if they apply in
// the current context (all apply if $record is false, since that
// means that no specific record ID was requested; otherwise, they only
// apply if the current record driver supports them):
$response = $this->createResponse();
$xml = $response->addChild('ListMetadataFormats');
foreach ($this->getMetadataFormats() as $prefix => $details) {
if (
$record === false
|| $record->getXML($prefix) !== false
|| ('oai_vufind_json' === $prefix && $this->supportsVuFindMetadata())
|| (self::OAI_FINNA_JSON === $prefix && $this->supportsFinnaMetadata())
) {
$node = $xml->addChild('metadataFormat');
$node->metadataPrefix = $prefix;
if (isset($details['schema'])) {
$node->schema = $details['schema'];
}
if (isset($details['namespace'])) {
$node->metadataNamespace = $details['namespace'];
}
}
}

// Display the response:
return $response->asXML();
}

/**
* Load data from the OAI section of config.ini. (This is called by the
* constructor and is only a separate method to allow easy override by child
* classes).
*
* @param \Laminas\Config\Config $config VuFind configuration
*
* @return void
*/
protected function initializeSettings(\Laminas\Config\Config $config)
{
parent::initializeSettings($config);
// Initialize Finna API format fields:
$this->finnaApiFields = array_filter(
explode(
',',
$config->OAI->finna_api_format_fields ?? ''
)
);
}

/**
* Support method for attachNonDeleted() to build the Finna metadata for
* a record driver.
*
* @param object $record A record driver object
*
* @return string
*/
protected function getFinnaMetadata($record)
{
// Root node
$recordDoc = new \DOMDocument();
$finnaFormat = $this->getMetadataFormats()[self::OAI_FINNA_JSON];
$rootNode = $recordDoc->createElementNS($finnaFormat['namespace'], self::OAI_FINNA_JSON . ':record');
$rootNode->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
$rootNode->setAttribute('xsi:schemaLocation', $finnaFormat['namespace'] . ' ' . $finnaFormat['schema']);
$recordDoc->appendChild($rootNode);

// Add oai_dc part
$oaiDc = new \DOMDocument();
$oaiDc->loadXML($record->getXML('oai_dc', $this->baseHostURL, $this->recordLinkerHelper));
$rootNode->appendChild($recordDoc->importNode($oaiDc->documentElement, true));

// Add Finna specific metadata
$records = $this->recordFormatter->format([$record], $this->finnaApiFields);
$metadataNode = $recordDoc->createElementNS($finnaFormat['namespace'], self::OAI_FINNA_JSON . ':metadata');
$metadataNode->setAttribute('type', 'application/json');
$metadataNode->appendChild($recordDoc->createCDATASection(json_encode($records[0])));
$rootNode->appendChild($metadataNode);

return $recordDoc->saveXML();
}
}
43 changes: 43 additions & 0 deletions module/Finna/src/Finna/RecordDriver/Feature/SolrFinnaTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,49 @@ public function getFirstIndexed()
return $this->fields['first_indexed'] ?? '';
}

/**
* Get an array containing media types as strings.
*
* @return array
*/
public function getMediaTypesAsStrings(): array
{
return array_map(
fn ($entry) => (string)$entry,
$this->fields['media_type_str_mv'] ?? []
);
}

/**
* Get array containing ctrlnum.
*
* @return array
*/
public function getCtrlNum(): array
{
return $this->fields['ctrlnum'] ?? [];
}

/**
* Get array containing major genres
*
* @return array
*/
public function getMajorGenres(): array
{
return $this->fields['major_genre_str_mv'] ?? [];
}

/**
* Get array containing Usage rights extended
*
* @return array
*/
public function getUsageRightsExt(): array
{
return $this->fields['usage_rights_ext_str_mv'] ?? [];
}

/**
* Is rating allowed.
*
Expand Down
Loading