/** * @covers PersonStringNlm30NameSchemaFilter * @covers Nlm30PersonStringFilter * @depends testExecuteWithSinglePersonString */ public function testExecuteWithMultiplePersonsStrings() { $personsString = 'MULLER:IFC Peterberg:Peters HC:Yu QK:Hans Peter B. Sperling:et al'; $expectedResults = array(array(null, null, null, 'Muller'), array(null, array('I', 'F', 'C'), null, 'Peterberg'), array(null, array('H', 'C'), null, 'Peters'), array(null, array('Q', 'K'), null, 'Yu'), array(null, array('Hans', 'Peter', 'B'), null, 'Sperling')); $personStringNlm30NameSchemaFilter = new PersonStringNlm30NameSchemaFilter(ASSOC_TYPE_AUTHOR, PERSON_STRING_FILTER_MULTIPLE); $personDescriptions =& $personStringNlm30NameSchemaFilter->execute($personsString); // The last description should be an 'et-al' string self::assertEquals(PERSON_STRING_FILTER_ETAL, array_pop($personDescriptions)); foreach ($personDescriptions as $testNumber => $personDescription) { $this->assertPerson($expectedResults[$testNumber], $personDescription, $testNumber); } // Test again, this time with title and degrees $personsString = 'Dr. MULLER; IFC Peterberg; Prof. Peters HC, MSc.; Yu QK;Hans Peter B. Sperling; etal'; $expectedResults = array(array('Dr.', null, null, 'Muller'), array(null, array('I', 'F', 'C'), null, 'Peterberg'), array('Prof. - MSc', array('H', 'C'), null, 'Peters'), array(null, array('Q', 'K'), null, 'Yu'), array(null, array('Hans', 'Peter', 'B'), null, 'Sperling')); $personStringNlm30NameSchemaFilter->setFilterTitle(true); $personStringNlm30NameSchemaFilter->setFilterDegrees(true); $personDescriptions =& $personStringNlm30NameSchemaFilter->execute($personsString); // The last description should be an 'et-al' string self::assertEquals(PERSON_STRING_FILTER_ETAL, array_pop($personDescriptions)); foreach ($personDescriptions as $testNumber => $personDescription) { $this->assertPerson($expectedResults[$testNumber], $personDescription, $testNumber); } // Test whether Vancouver style comma separation works correctly $personsString = 'Peterberg IFC, Peters HC, Sperling HP'; $expectedResults = array(array(null, array('I', 'F', 'C'), null, 'Peterberg'), array(null, array('H', 'C'), null, 'Peters'), array(null, array('H', 'P'), null, 'Sperling')); $personStringNlm30NameSchemaFilter->setFilterTitle(false); $personStringNlm30NameSchemaFilter->setFilterDegrees(false); $personDescriptions =& $personStringNlm30NameSchemaFilter->execute($personsString); foreach ($personDescriptions as $testNumber => $personDescription) { $this->assertPerson($expectedResults[$testNumber], $personDescription, $testNumber); } // Single name strings should not be cut when separated by comma. $personsString = 'Willinsky, John'; $expectedResult = array(null, array('John'), null, 'Willinsky'); $personDescriptions =& $personStringNlm30NameSchemaFilter->execute($personsString); $this->assertEquals(1, count($personDescriptions)); $this->assertPerson($expectedResult, $personDescriptions[0], $testNumber); // Test APA style author tokenization. $singleAuthor = array(1 => 'Berndt, T. J.'); $twoAuthors = array(2 => 'Wegener-Prent, D. T., & Petty, R. E.'); $threeToSevenAuthors = array(6 => 'Kernis Wettelberger, M. H., Cornell, D. P., Sun, C. R., Berry, A., Harlow, T., & Bach, J. S.'); $moreThanSevenAuthors = array(7 => 'Miller, F. H., Choi, M.J., Angeli, L. L., Harland, A. A., Stamos, J. A., Thomas, S. T., . . . Rubin, L. H.'); $singleEditor = array(1 => 'A. Editor'); $twoEditors = array(2 => 'A. Editor-Double & B. Editor'); $threeToSevenEditors = array(6 => 'M.H. Kernis Wettelberger, D. P. Cornell, C.R. Sun, A. Berry, T. Harlow & J.S. Bach'); $moreThanSevenEditors = array(7 => 'F. H. Miller, M. J. Choi, L. L. Angeli, A. A. Harland, J. A. Stamos, S. T. Thomas . . . L. H. Rubin'); foreach (array($singleAuthor, $twoAuthors, $threeToSevenAuthors, $moreThanSevenAuthors, $singleEditor, $twoEditors, $threeToSevenEditors, $moreThanSevenEditors) as $test) { $expectedNumber = key($test); $testString = current($test); $personDescriptions =& $personStringNlm30NameSchemaFilter->execute($testString); $this->assertEquals($expectedNumber, count($personDescriptions), 'Offending string: ' . $testString); } }
/** * Map OpenURL properties to NLM properties. * NB: OpenURL has no i18n so we use the default * locale when mapping. * @see Filter::process() * @param $input MetadataDescription * @return MetadataDescription */ function &process(&$input) { $nullVar = null; // Instantiate the target description. $output = new MetadataDescription('lib.pkp.plugins.metadata.nlm30.schema.Nlm30CitationSchema', $input->getAssocType()); // Parse au statements into name descriptions import('lib.pkp.plugins.metadata.nlm30.filter.PersonStringNlm30NameSchemaFilter'); $personStringFilter = new PersonStringNlm30NameSchemaFilter(ASSOC_TYPE_AUTHOR); $authors =& $input->getStatement('au'); if (is_array($authors) && count($authors)) { // TODO: We might improve results here by constructing the // first author from aufirst, aulast fields. foreach ($authors as $author) { $authorDescription =& $personStringFilter->execute($author); $success = $output->addStatement('person-group[@person-group-type="author"]', $authorDescription); assert($success); unset($authorDescription); } } // Publication type $publicationType = null; if ($input->hasStatement('genre')) { $genre = $input->getStatement('genre'); $genreMap = $this->_getOpenurl10GenreTranslationMapping(); $publicationType = isset($genreMap[$genre]) ? $genreMap[$genre] : $genre; $success = $output->addStatement('[@publication-type]', $publicationType); assert($success); } // Get NLM => OpenURL property mapping. $propertyMap =& $this->nlmOpenurl10Mapping($publicationType, $input->getMetadataSchema()); // Transfer mapped properties with default locale foreach ($propertyMap as $nlm30Property => $openurl10Property) { if ($input->hasStatement($openurl10Property)) { $success = $output->addStatement($nlm30Property, $input->getStatement($openurl10Property)); assert($success); } } return $output; }
/** * Custom implementation of Form::validate() that validates * meta-data form data and injects it into the internal citation * object. * * NB: The configuration of the internal citation object * would normally be done in readInputData(). Validation and * injection can easily be done in one step. It therefore avoids * code duplication and improves performance to do both here. */ function validate() { // Make sure that this method is not called twice which // would corrupt internal state. assert(empty($this->_metadataDescriptions)); parent::validate(); // Validate form data and inject it into // the associated citation object. $citation =& $this->getCitation(); $citation->setRawCitation($this->getData('rawCitation')); if ($this->getData('citationApproved') == 'citationApproved') { // Editor's shortcut to the approved state, e.g. for manually edited citations. $citation->setCitationState(CITATION_APPROVED); } elseif (in_array($this->getData('citationState'), Citation::_getSupportedCitationStates())) { // Reset citation state if necessary if ($this->getData('citationState') == CITATION_APPROVED) { $this->setData('citationState', CITATION_LOOKED_UP); } $citation->setCitationState($this->getData('citationState')); } // Extract data from citation form fields and inject it into the citation import('lib.pkp.classes.metadata.MetadataDescription'); $metadataSchemas = $citation->getSupportedMetadataSchemas(); foreach ($metadataSchemas as $metadataSchema) { /* @var $metadataSchema MetadataSchema */ // Instantiate a meta-data description for the given schema $metadataDescription = new MetadataDescription($metadataSchema->getClassName(), ASSOC_TYPE_CITATION); // Set the meta-data statements foreach ($metadataSchema->getProperties() as $propertyName => $property) { $fieldName = $metadataSchema->getNamespacedPropertyId($propertyName); $fieldValue = trim($this->getData($fieldName)); if (empty($fieldValue)) { // Delete empty statements so that previously set // statements (if any) will be deleted. $metadataDescription->removeStatement($propertyName); if ($property->getMandatory()) { // A mandatory field is missing - add a validation error. $this->addError($fieldName, __($property->getValidationMessage())); $this->addErrorField($fieldName); } } else { // Try to convert the field value to (a) strongly // typed object(s) if applicable. Start with the most // specific allowed type so that we always get the // most strongly typed result possible. $allowedTypes = $property->getAllowedTypes(); switch (true) { case isset($allowedTypes[METADATA_PROPERTY_TYPE_VOCABULARY]) && is_numeric($fieldValue): case isset($allowedTypes[METADATA_PROPERTY_TYPE_INTEGER]) && is_numeric($fieldValue): $typedFieldValues = array((int) $fieldValue); break; case isset($allowedTypes[METADATA_PROPERTY_TYPE_DATE]): import('lib.pkp.classes.metadata.DateStringNormalizerFilter'); $dateStringFilter = new DateStringNormalizerFilter(); assert($dateStringFilter->supportsAsInput($fieldValue)); $typedFieldValues = array($dateStringFilter->execute($fieldValue)); break; case isset($allowedTypes[METADATA_PROPERTY_TYPE_COMPOSITE]): // We currently only support name composites $allowedAssocIds = $allowedTypes[METADATA_PROPERTY_TYPE_COMPOSITE]; if (in_array(ASSOC_TYPE_AUTHOR, $allowedAssocIds)) { $assocType = ASSOC_TYPE_AUTHOR; } elseif (in_array(ASSOC_TYPE_EDITOR, $allowedAssocIds)) { $assocType = ASSOC_TYPE_EDITOR; } else { assert(false); } // Try to transform the field to a name composite. import('lib.pkp.plugins.metadata.nlm30.filter.PersonStringNlm30NameSchemaFilter'); $personStringFilter = new PersonStringNlm30NameSchemaFilter($assocType, PERSON_STRING_FILTER_MULTIPLE); assert($personStringFilter->supportsAsInput($fieldValue)); $typedFieldValues =& $personStringFilter->execute($fieldValue); break; default: $typedFieldValues = array($fieldValue); } // Inject data into the meta-data description and thereby // implicitly validate the field value. foreach ($typedFieldValues as $typedFieldValue) { if (!$metadataDescription->addStatement($propertyName, $typedFieldValue)) { // Add form field error $this->addError($fieldName, __($property->getValidationMessage())); $this->addErrorField($fieldName); } unset($typedFieldValue); } unset($typedFieldValues); } } // Inject the meta-data into the citation. $citation->injectMetadata($metadataDescription); // Save the meta-data description for later usage. $this->_metadataDescriptions[] =& $metadataDescription; unset($metadataDescription); } return $this->isValid(); }
/** * Post processes an NLM meta-data array * @param $preliminaryNlm30Array array * @return array */ function &postProcessMetadataArray(&$preliminaryNlm30Array) { // Clean array $preliminaryNlm30Array = arrayClean($preliminaryNlm30Array); // Trim punctuation $preliminaryNlm30Array =& $this->_recursivelyTrimPunctuation($preliminaryNlm30Array); // Parse (=filter) author/editor strings into NLM name descriptions foreach (array('author' => ASSOC_TYPE_AUTHOR, 'editor' => ASSOC_TYPE_EDITOR) as $personType => $personAssocType) { if (isset($preliminaryNlm30Array[$personType])) { // Get the author/editor strings from the result $personStrings = $preliminaryNlm30Array[$personType]; unset($preliminaryNlm30Array[$personType]); // Parse the author/editor strings into NLM name descriptions // Interpret a scalar as a textual authors list if (is_scalar($personStrings)) { $personStringFilter = new PersonStringNlm30NameSchemaFilter($personAssocType, PERSON_STRING_FILTER_MULTIPLE); $persons =& $personStringFilter->execute($personStrings); } else { $personStringFilter = new PersonStringNlm30NameSchemaFilter($personAssocType, PERSON_STRING_FILTER_SINGLE); $persons =& array_map(array($personStringFilter, 'execute'), $personStrings); } $preliminaryNlm30Array['person-group[@person-group-type="' . $personType . '"]'] = $persons; unset($persons); } } // Join comments if (isset($preliminaryNlm30Array['comment']) && is_array($preliminaryNlm30Array['comment'])) { // Implode comments from the result into a single string // as required by the NLM citation schema. $preliminaryNlm30Array['comment'] = implode("\n", $preliminaryNlm30Array['comment']); } // Normalize date strings foreach (array('date', 'conf-date', 'access-date') as $dateProperty) { if (isset($preliminaryNlm30Array[$dateProperty])) { $dateFilter = new DateStringNormalizerFilter(); $preliminaryNlm30Array[$dateProperty] = $dateFilter->execute($preliminaryNlm30Array[$dateProperty]); } } // Cast strings to integers where necessary foreach (array('fpage', 'lpage', 'size') as $integerProperty) { if (isset($preliminaryNlm30Array[$integerProperty]) && is_numeric($preliminaryNlm30Array[$integerProperty])) { $preliminaryNlm30Array[$integerProperty] = (int) $preliminaryNlm30Array[$integerProperty]; } } // Rename elements that are stored in attributes in NLM citation $elementToAttributeMap = array('access-date' => 'date-in-citation[@content-type="access-date"]', 'issn-ppub' => 'issn[@pub-type="ppub"]', 'issn-epub' => 'issn[@pub-type="epub"]', 'pub-id-doi' => 'pub-id[@pub-id-type="doi"]', 'pub-id-publisher-id' => 'pub-id[@pub-id-type="publisher-id"]', 'pub-id-coden' => 'pub-id[@pub-id-type="coden"]', 'pub-id-sici' => 'pub-id[@pub-id-type="sici"]', 'pub-id-pmid' => 'pub-id[@pub-id-type="pmid"]', 'publication-type' => '[@publication-type]'); foreach ($elementToAttributeMap as $elementName => $nlm30PropertyName) { if (isset($preliminaryNlm30Array[$elementName])) { $preliminaryNlm30Array[$nlm30PropertyName] = $preliminaryNlm30Array[$elementName]; unset($preliminaryNlm30Array[$elementName]); } } // Guess a publication type if none has been set by the // citation service. $this->_guessPublicationType($preliminaryNlm30Array); // Some services return the title as article-title although // the publication type is a book. if (isset($preliminaryNlm30Array['[@publication-type]']) && $preliminaryNlm30Array['[@publication-type]'] == 'book') { if (isset($preliminaryNlm30Array['article-title']) && !isset($preliminaryNlm30Array['source'])) { $preliminaryNlm30Array['source'] = $preliminaryNlm30Array['article-title']; unset($preliminaryNlm30Array['article-title']); } } return $preliminaryNlm30Array; }