* @author Stefan Rohde-Enslin */ declare(strict_types = 1); /** * Publication Quality Index: This class provides the base for computing a * numeric score for describing the quality of an object record for publication. */ final class MDPuqi { // Just by itself, the PuQI score basically starts at around -60. // To not frustrate users, it thus makes sense to simply add a reasonable // number to get scores starting in the upper 2 digit numbers. public const PUBLICATION_ADDEND_FOR_SCORE = 171; // Limits for word counts private const THRESHOLD_WC_OBJECT_TYPE_TOO_LONG = 2; private const THRESHOLD_WC_OBJECT_TITLE_TOO_LONG = 10; // Character count private const THRESHOLD_CC_OBJECT_DESC_TOO_SHORT = 50; private const THRESHOLD_CC_OBJECT_DESC_SHORT = 250; private const THRESHOLD_CC_OBJECT_DESC_GOOD = 900; private const THRESHOLD_CC_OBJECT_DESC_GREAT = 1750; private const THRESHOLD_CC_OBJECT_DESC_QUITE_LONG = 5000; public const THRESHOLD_TAGS_TOO_MANY = 10; public const THRESHOLD_MANY_IMAGES = 10; public const THRESHOLD_IMG_TOO_SMALL = 600; // px public const THRESHOLD_IMG_SMALL = 800; // px public const THRESHOLD_IMG_GOOD_SIZE_SHORTER = 1200; // px public const THRESHOLD_IMG_GOOD_SIZE_LONGER = 1920; // px public const LICENSES_OPEN_ACCESS_PD = ["CC0", "Public Domain Mark", "Orphan Work"]; public const LICENSES_OPEN_ACCESS_ATTRIBUTION = ["CC BY"]; public const LICENSES_CLOSED_ACCESS = ["RR-F", "RR-P", "RR-R"]; public const QI_REWARD_DESCRIPTION_LENGTH_GOOD = 3; private const QI_REWARD_DESCRIPTION_LENGTH_GREAT = 6; private const QI_REWARD_EVENTS_MULTIPLIER = 5; public const QI_REWARD_TAGS_MULTIPLIER = 3; public const QI_REWARD_TAGS_MULTIPLIER_ABOVE_THRESHOLD = 1; private const QI_REWARD_LITERATURE_MULTIPLIER = 2; private const QI_REWARD_HYPERLINKS_MULTIPLIER = 2; private const QI_REWARD_DOCUMENTS_MULTIPLIER = 1; private const QI_REWARD_LINKED_OBJS_MULTIPLIER = 3; private const QI_REWARD_SERIES_MULTIPLIER = 4; private const QI_REWARD_EXHIBITIONS_MULTIPLIER = 4; private const QI_REWARD_TRANSLATIONS_MULTIPLIER = 10; private const QI_REWARD_TRANSCRIPTS_MULTIPLIER = 10; private const QI_REWARD_OBJ_IS_REF_MULTIPLIER = 5; private const QI_REWARD_OBJ_RECEPTION_MULTIPLIER = 1; private const QI_REWARD_MARKINGS_MULTIPLIER = 3; public const QI_REWARD_IMGS_RESOURCES_MULTIPLIER = 3; public const QI_REWARD_IMAGE_SIZE_LARGE = 1; private const QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS = 2; private const QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS_ATTRIBUTION = 1; private const QI_REWARD_METADATA_LICENCE_OPEN_ACCESS = 5; private const QI_REWARD_METADATA_LICENCE_OPEN_ACCESS_ATTRIBUTION = 3; private const QI_REWARD_PUBLIC_INSCRIPTION = 3; private const QI_REWARD_PUBLIC_COMPARABLE_OBJECTS = 3; private const QI_REWARD_PUBLIC_DETAILED_DESCRIPTION = 5; private const QI_PENALTY_EMPTY_OBJECT_TYPE = -20; private const QI_PENALTY_TITLE_ONE_WORD = -3; private const QI_PENALTY_TITLE_DUPLICATE = -5; private const QI_PENALTY_TITLE_TOO_LONG = -5; private const QI_PENALTY_DESCRIPTION_DUPLICATE = -25; private const QI_PENALTY_DESCRIPTION_TOO_SHORT = -10; private const QI_PENALTY_DESCRIPTION_SHORT = -5; private const QI_PENALTY_DESCRIPTION_TOO_LONG = -6; private const QI_PENALTY_EMPTY_MATTECH = -3; private const QI_PENALTY_EMPTY_MEASUREMENTS = -5; private const QI_PENALTY_NO_IMAGE = -10; private const QI_PENALTY_NO_COLLECTION = -10; private const QI_PENALTY_MULTIPLIER_EVENT_PART_DOUBLE_LINKED_AS_RELATED = -10; public const QI_PENALTY_NO_EVENTS = -15; public const QI_PENALTY_NO_TAG = -15; public const QI_PENALTY_ONLY_ONE_TAG = -10; public const QI_PENALTY_ONLY_TWO_TAG = -5; private const QI_PENALTY_IMAGE_FILE_MISSING = -5; public const QI_PENALTY_IMAGE_TOO_SMALL = -5; public const QI_PENALTY_IMAGE_SMALL = -2; private const QI_PENALTY_IMAGE_NO_OWNER_NO_LICENCE = -15; private const QI_PENALTY_IMAGE_NO_OWNER = -10; private const QI_PENALTY_IMAGE_NO_LICENCE = -10; private const QI_PENALTY_IMAGE_LICENCE_CLOSED_ACCESS = -1; private const QI_PENALTY_METADATA_NO_LICENCE = -10; private const QI_PENALTY_METADATA_LICENCE_CLOSED_ACCESS = -3; public const INDICATOR_THRESHOLD_COLOR_RED = -30; public const INDICATOR_THRESHOLD_COLOR_BLUE = 10; private MDTlLoader $_tlLoader; /** @var array */ private array $_messages = []; /** @var array */ private array $_checkedSections = []; /** * Function count words returns a word count * * @param string $string String to get word count ffor. * * @return integer */ private function _count_words(string $string):int { $string = htmlspecialchars_decode(\strip_tags($string)); if (empty($string)) return 0; $t = ['_' => 1, "\x20" => 1, "\xA0" => 1, "\x0A" => 1, "\x0D" => 1, "\x09" => 1, "\x0B" => 1, "\x2E" => 1, '=' => 1, '+' => 1, '-' => 1, '*' => 1, '/' => 1, '\\' => 1, ',' => 1, ';' => 1, ':' => 1, '"' => 1, '\'' => 1, '[' => 1, ']' => 1, '{' => 1, '}' => 1, '(' => 1, ')' => 1, '<' => 1, '>' => 1, '&' => 1, '%' => 1, '$' => 1, '@' => 1, '#' => 1, '^' => 1, '!' => 1, '?' => 1]; // separators $count = isset($t[$string[0]]) ? 0 : 1; $inpStrLen = strlen($string); if ($inpStrLen === 1) return $count; for ($i = 1; $i < $inpStrLen; $i++) { if (isset($t[$string[$i - 1]]) && !isset($t[$string[$i]])) // if new word starts $count++; } return $count; } /** * Marks the object name as a duplicate. * * @param integer $numberOfDuplicates Number of duplicates. * * @return void */ public function markObjectNameAsDuplicate(int $numberOfDuplicates):void { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectName, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "one_common_word_objecttitle") . ' (' . $numberOfDuplicates . ' ' . $this->_tlLoader->tl("quality", "quality", "objects_same_title") . ')', self::QI_PENALTY_TITLE_DUPLICATE, ); } /** * Marks the object description as a duplicate. * * @return void */ public function markObjectDescriptionAsDuplicate():void { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectDescription, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "object_description_double") . '!', self::QI_PENALTY_DESCRIPTION_DUPLICATE, ); } /** * Checks the object's name. * * @param string $object_name Name of the object. * * @return void */ public function checkObjectName(string $object_name):void { $this->_checkedSections[] = MDPuqiCheckSection::objectName; $words_in_title = $this->_count_words($object_name); if ($words_in_title === 1) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectName, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "one_word_object_title"), self::QI_PENALTY_TITLE_ONE_WORD, ); } else if ($words_in_title > self::THRESHOLD_WC_OBJECT_TITLE_TOO_LONG) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectName, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "object_title_too_long"), self::QI_PENALTY_TITLE_TOO_LONG, ); } } /** * Checks the object's type. * * @param string $object_type Type of the object. * * @return void */ public function checkObjectType(string $object_type):void { $this->_checkedSections[] = MDPuqiCheckSection::objectType; if (empty($object_type)) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectType, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_objecttype") . '!', self::QI_PENALTY_EMPTY_OBJECT_TYPE, ); } else if ($this->_count_words($object_type) > self::THRESHOLD_WC_OBJECT_TYPE_TOO_LONG) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectType, MDPuqiMessageStatus::neutral, $this->_count_words($object_type) . ' ' . $this->_tlLoader->tl("quality", "quality", "many_words_object_type"), 0, ); } } /** * Checks the object's description's length. * * @param string $object_description Description of the object. * * @return void */ public function checkObjectDescriptionLength(string $object_description):void { $this->_checkedSections[] = MDPuqiCheckSection::objectDescription; $objDescLength = mb_strlen($object_description); if ($objDescLength < self::THRESHOLD_CC_OBJECT_DESC_TOO_SHORT) { $msg = $objDescLength . ' ' . $this->_tlLoader->tl("quality", "quality", "object_description_characters") . '. ' . $this->_tlLoader->tl("quality", "quality", "too_short") . '!'; $status = MDPuqiMessageStatus::warning; $value = self::QI_PENALTY_DESCRIPTION_TOO_SHORT; } else if ($objDescLength < self::THRESHOLD_CC_OBJECT_DESC_SHORT) { $msg = $objDescLength . ' ' . $this->_tlLoader->tl("quality", "quality", "object_description_characters") . '. ' . $this->_tlLoader->tl("quality", "quality", "quite_short") . '!'; $status = MDPuqiMessageStatus::warning; $value = self::QI_PENALTY_DESCRIPTION_SHORT; } else if ($objDescLength < self::THRESHOLD_CC_OBJECT_DESC_GOOD) { $msg = $objDescLength . ' ' . $this->_tlLoader->tl("quality", "quality", "object_description_characters") . '. ' . $this->_tlLoader->tl("quality", "quality", "good") . '!'; $status = MDPuqiMessageStatus::praise; $value = self::QI_REWARD_DESCRIPTION_LENGTH_GOOD; } else if ($objDescLength < self::THRESHOLD_CC_OBJECT_DESC_GREAT) { $msg = $objDescLength . ' ' . $this->_tlLoader->tl("quality", "quality", "object_description_characters") . '. ' . $this->_tlLoader->tl("quality", "quality", "excellent") . '!'; $status = MDPuqiMessageStatus::praise; $value = self::QI_REWARD_DESCRIPTION_LENGTH_GREAT; } else if ($objDescLength < self::THRESHOLD_CC_OBJECT_DESC_QUITE_LONG) { $msg = $objDescLength . ' ' . $this->_tlLoader->tl("quality", "quality", "object_description_characters") . ' ' . $this->_tlLoader->tl("quality", "quality", "quite_long") . '!'; $status = MDPuqiMessageStatus::neutral; $value = 0; } else { $msg = $objDescLength . ' ' . $this->_tlLoader->tl("quality", "quality", "object_description_characters") . ' ' . $this->_tlLoader->tl("quality", "quality", "too_long") . '!'; $status = MDPuqiMessageStatus::warning; $value = self::QI_PENALTY_DESCRIPTION_TOO_LONG; } $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectDescription, $status, $msg, $value, ); } /** * Checks the object's material / technique. * * @param string $object_material_tech Material / technique of the object. * * @return void */ public function checkObjectMaterialTechnique(string $object_material_tech):void { $this->_checkedSections[] = MDPuqiCheckSection::objectMaterialTechnique; if (empty($object_material_tech)) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectMaterialTechnique, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_mattech") . '.', self::QI_PENALTY_EMPTY_MATTECH, ); } } /** * Checks the object's material / technique. * * @param string $object_material_tech Material / technique of the object. * * @return void */ public function checkObjectMeasurements(string $object_material_tech):void { $this->_checkedSections[] = MDPuqiCheckSection::objectMeasurements; if (empty($object_material_tech)) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectMeasurements, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_dimensions") . '.', self::QI_PENALTY_EMPTY_MEASUREMENTS, ); } } /** * Checks events. * * @param array $events Events. * @param array $relatedActors List of related actors. * @param array $relatedPlaces List of related places. * @param array $relatedTimes List of related times. * @param integer $noOfTags Number of tags linked to the object. * * @return void */ public function checkEvents(array $events, array $relatedActors, array $relatedPlaces, array $relatedTimes, int $noOfTags):void { $this->_checkedSections[] = MDPuqiCheckSection::events; $this->_checkedSections[] = MDPuqiCheckSection::tags; $eventActors = $eventPlaces = $eventTimes = []; foreach ($events as $event) { if (!empty($event['actor'])) $eventActors[] = $event['actor']; if (!empty($event['place'])) $eventPlaces[] = $event['place']; if (!empty($event['time'])) $eventTimes[] = $event['time']; } /* * Check the number of linked events */ $events_count = count($events); if ($events_count === 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::events, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_event") . '.', self::QI_PENALTY_NO_EVENTS, ); } else { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::events, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "multiple_event") . '.', self::QI_REWARD_EVENTS_MULTIPLIER * min($events_count, 10), ); } /* * Check for regular related entries being linked as part of events as well. */ $duplicateRelatedCount = 0; foreach ($relatedActors as $entry) { if (in_array($entry, $eventActors, true)) { $duplicateRelatedCount++; } } foreach ($relatedPlaces as $entry) { if (in_array($entry, $eventPlaces, true)) { $duplicateRelatedCount++; } } foreach ($relatedTimes as $entry) { if (in_array($entry, $eventTimes, true)) { $duplicateRelatedCount++; } } if ($duplicateRelatedCount !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::events, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "opz_event_tag") . '.', self::QI_PENALTY_MULTIPLIER_EVENT_PART_DOUBLE_LINKED_AS_RELATED * $duplicateRelatedCount, ); } /** * Check number of tags and related actors / places / times */ $anzahl_swoergebnis = $noOfTags + count($relatedActors) + count($relatedPlaces) + count($relatedTimes); if ($anzahl_swoergebnis === 0) { $msg = $this->_tlLoader->tl("quality", "quality", "no_tag"); $status = MDPuqiMessageStatus::warning; $value = self::QI_PENALTY_NO_TAG; } else if ($anzahl_swoergebnis === 1) { $msg = $this->_tlLoader->tl("quality", "quality", "one_tag"); $status = MDPuqiMessageStatus::warning; $value = self::QI_PENALTY_ONLY_ONE_TAG; } else if ($anzahl_swoergebnis === 2) { $msg = $this->_tlLoader->tl("quality", "quality", "two_tags"); $status = MDPuqiMessageStatus::warning; $value = self::QI_PENALTY_ONLY_TWO_TAG; } else if ($anzahl_swoergebnis < self::THRESHOLD_TAGS_TOO_MANY) { $msg = $anzahl_swoergebnis . ' ' . $this->_tlLoader->tl("quality", "quality", "tree_nine__tags"); $status = MDPuqiMessageStatus::praise; $value = self::QI_REWARD_TAGS_MULTIPLIER * $anzahl_swoergebnis; } else { $msg = $this->_tlLoader->tl("quality", "quality", "tree_nine__tags"); $status = MDPuqiMessageStatus::praise; $value = self::QI_REWARD_TAGS_MULTIPLIER * self::THRESHOLD_TAGS_TOO_MANY + ($anzahl_swoergebnis - self::THRESHOLD_TAGS_TOO_MANY) * self::QI_REWARD_TAGS_MULTIPLIER_ABOVE_THRESHOLD; } $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::tags, $status, $msg, $value, ); } /** * Checks the number of linked collections. * * @param integer $count Number of linked collections. * * @return void */ public function checkCollectionCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::collectionCount; if ($count === 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::collectionCount, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_collection") . '.', self::QI_PENALTY_NO_COLLECTION, ); } } /** * Checks the number of linked literature entries. * * @param integer $count Number of linked literature entries. * * @return void */ public function checkLiteratureCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::literatureCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::literatureCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "literature_asigned"), self::QI_REWARD_LITERATURE_MULTIPLIER * $count, ); } } /** * Checks the number of linked hyperlinks. * * @param integer $count Number of linked hyperlinks. * * @return void */ public function checkHyperlinkCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::hyperlinkCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::hyperlinkCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "online_resources_asigned"), self::QI_REWARD_HYPERLINKS_MULTIPLIER * $count, ); } } /** * Checks the number of linked documents. * * @param integer $count Number of linked documents. * * @return void */ public function checkDocumentCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::documentCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::documentCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "documents_asigned"), self::QI_REWARD_DOCUMENTS_MULTIPLIER * $count, ); } } /** * Checks the number of linked objects. * * @param integer $count Number of linked objects. * * @return void */ public function checkLinkedObjectCountount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::linkedObjectCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::linkedObjectCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "object_relations_asigned"), self::QI_REWARD_LINKED_OBJS_MULTIPLIER * $count, ); } } /** * Checks the number of linked series. * * @param integer $count Number of linked series. * * @return void */ public function checkSeriesCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::seriesCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::seriesCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "objectgroup_asigned"), self::QI_REWARD_SERIES_MULTIPLIER * $count, ); } } /** * Checks the number of linked exhibitions. * * @param integer $count Number of linked exhibitions. * * @return void */ public function checkExhibitionCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::exhibitionCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::exhibitionCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "exhibition_assigned"), self::QI_REWARD_EXHIBITIONS_MULTIPLIER * $count, ); } } /** * Checks the number of linked translations. * * @param integer $count Number of linked translations. * * @return void */ public function checkTranslationCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::translationCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::translationCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "translations_exist"), self::QI_REWARD_TRANSLATIONS_MULTIPLIER * $count, ); } } /** * Checks the number of linked transcripts. * * @param integer $count Number of linked transcripts. * * @return void */ public function checkTranscriptCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::transcriptCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::transcriptCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "transcripts_exist"), self::QI_REWARD_TRANSCRIPTS_MULTIPLIER * $count, ); } } /** * Checks the number of linked reception publications. * * @param integer $count Number of linked reception publications. * * @return void */ public function checkObjectHasReceptionPublicationsCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::objectHasReceptionCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectHasReceptionCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "reception_publications_recorded"), self::QI_REWARD_OBJ_RECEPTION_MULTIPLIER * $count, ); } } /** * Checks the number of linked sources the object references. * * @param integer $count Number of linked sources the object references. * * @return void */ public function checkObjectIsReferenceCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::objectIsReferenceCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::objectIsReferenceCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "is_reference_entry_exists"), self::QI_REWARD_OBJ_IS_REF_MULTIPLIER * $count, ); } } /** * Checks the number of linked markings. * * @param integer $count Number of linked markings. * * @return void */ public function checkMarkingCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::markingCount; if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::markingCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "markings_exist"), self::QI_REWARD_MARKINGS_MULTIPLIER * $count, ); } } /** * Checks the number of linked images / media. * * @param integer $count Number of linked images. * * @return void */ public function checkImageCount(int $count):void { $this->_checkedSections[] = MDPuqiCheckSection::imageCount; switch ($count) { case 0: $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageCount, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_image"), self::QI_PENALTY_NO_IMAGE, ); break; case 1: $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "one_image"), self::QI_REWARD_IMGS_RESOURCES_MULTIPLIER, ); break; default: $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageCount, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "many_images"), self::QI_REWARD_IMGS_RESOURCES_MULTIPLIER * min(self::THRESHOLD_MANY_IMAGES, $count), ); break; } } /** * Assesses the availability of public inscription status. * * @param boolean $available Availability of inscription status. * * @return void */ public function checkPublicInscriptionAvailability(bool $available):void { $this->_checkedSections[] = MDPuqiCheckSection::publicInscription; if ($available === true) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::publicInscription, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "has_public_inscription"), self::QI_REWARD_PUBLIC_INSCRIPTION, ); } } /** * Assesses the availability of public detailed description. * * @param boolean $available Availability of detailed description. * * @return void */ public function checkPublicDetailedDescriptionAvailability(bool $available):void { $this->_checkedSections[] = MDPuqiCheckSection::publicDetailedDescription; if ($available === true) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::publicDetailedDescription, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "has_public_detailed_description"), self::QI_REWARD_PUBLIC_DETAILED_DESCRIPTION, ); } } /** * Assesses the availability of public comparable objects information. * * @param boolean $available Availability of comparable objects information. * * @return void */ public function checkPublicComparableObjectsAvailability(bool $available):void { $this->_checkedSections[] = MDPuqiCheckSection::publicComparableObjects; if ($available === true) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::publicComparableObjects, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "has_public_comparable_objs"), self::QI_REWARD_PUBLIC_COMPARABLE_OBJECTS, ); } } /** * Assesses the metadata rights status. * * @param string $license Metadata license. * * @return void */ public function checkMetadataLicense(string $license):void { $this->_checkedSections[] = MDPuqiCheckSection::metadataLicense; if (empty($license)) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::metadataLicense, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("basis", "basis", "metadata") . ': ' . $this->_tlLoader->tl("quality", "quality", "no_rightsstatus"), self::QI_PENALTY_METADATA_NO_LICENCE, ); } else if (in_array($license, self::LICENSES_OPEN_ACCESS_PD, true)) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::metadataLicense, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("basis", "basis", "metadata") . ': ' . $this->_tlLoader->tl("quality", "quality", "open_access_licence_used"), self::QI_REWARD_METADATA_LICENCE_OPEN_ACCESS, ); } else if (in_array($license, self::LICENSES_OPEN_ACCESS_ATTRIBUTION, true)) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::metadataLicense, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("basis", "basis", "metadata") . ': ' . $this->_tlLoader->tl("quality", "quality", "open_access_licence_used"), self::QI_REWARD_METADATA_LICENCE_OPEN_ACCESS_ATTRIBUTION, ); } else if (in_array($license, self::LICENSES_CLOSED_ACCESS, true)) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::metadataLicense, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("basis", "basis", "metadata") . ': ' . $this->_tlLoader->tl("quality", "quality", "restrictive_licence_used"), self::QI_PENALTY_METADATA_LICENCE_CLOSED_ACCESS, ); } else if (!in_array($license, MDLicensesSet::AVAILABLE_LICENSES_NAMES, true)) { throw new Exception("Thus far uncategorized license statement: " . $license); } } /** * Sets a penalty for missing images. * * @param integer $count Number of missing images. * * @return void */ public function markMissingImages(int $count):void { if ($count !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageMissing, MDPuqiMessageStatus::warning, 'Image does not exist (' . $count . ')', self::QI_PENALTY_IMAGE_FILE_MISSING * $count, ); } } /** * Evaluates a single image license. * * @param array $sizes Image sizes. * * @return void */ public function checkImageSizes(array $sizes):void { $this->_checkedSections[] = MDPuqiCheckSection::imageSizes; $imgsTooSmall = $imgsSmall = $imgsLarge = 0; foreach ($sizes as $image) { // Skip non-existent images if ($image['width'] === 0 && $image['height'] === 0) return; $img_side_shorter = \min($image['width'], $image['height']); $img_side_longer = \max($image['width'], $image['height']); if ($img_side_longer < self::THRESHOLD_IMG_TOO_SMALL) { $imgsTooSmall++; } else if ($img_side_longer < self::THRESHOLD_IMG_SMALL) { $imgsSmall++; } else if ($img_side_shorter >= self::THRESHOLD_IMG_GOOD_SIZE_SHORTER && $img_side_longer >= self::THRESHOLD_IMG_GOOD_SIZE_LONGER ) { $imgsLarge++; } } if ($imgsTooSmall !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageSizes, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "image_too_small") . ' (' . $imgsTooSmall . ')', self::QI_PENALTY_IMAGE_TOO_SMALL * min($imgsTooSmall, self::THRESHOLD_MANY_IMAGES), ); } if ($imgsSmall !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageSizes, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "image_quite_small") . ' (' . $imgsSmall . ')', self::QI_PENALTY_IMAGE_SMALL * min($imgsSmall, self::THRESHOLD_MANY_IMAGES), ); } if ($imgsLarge !== 0) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageSizes, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "image_has_good_size") . ' (' . $imgsLarge . ')', self::QI_REWARD_IMAGE_SIZE_LARGE * min($imgsLarge, self::THRESHOLD_MANY_IMAGES), ); } } /** * Evaluates an image's license. * * @param array $rightsstatus Rights status of the image. * * @return void */ public function checkImageLicenses(array $rightsstatus):void { $this->_checkedSections[] = MDPuqiCheckSection::imageLicenses; $license_pd = $no_license_or_rightsholder = $no_rightsholder = $no_license = $license_open_access_attribution = $license_closed_access = 0; foreach ($rightsstatus as $cur) { if (in_array($cur['license'], self::LICENSES_OPEN_ACCESS_PD, true)) { $license_pd++; } if (empty($cur['rightsholder']) and empty($cur['license'])) { $no_license_or_rightsholder++; } else if (empty($cur['rightsholder']) && $cur['license'] !== 'Public Domain Mark') { $no_rightsholder++; } else if (empty($cur['license'])) { $no_license++; } else if (in_array($cur['license'], self::LICENSES_OPEN_ACCESS_ATTRIBUTION, true)) { $license_open_access_attribution++; } else if (in_array($cur['license'], self::LICENSES_CLOSED_ACCESS, true)) { $license_closed_access++; } else if (!in_array($cur['license'], MDLicensesSet::AVAILABLE_LICENSES_NAMES, true)) { throw new Exception("Thus far uncategorized license statement: " . $cur['license']); } } if ($license_pd) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageLicenses, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "open_access_licence_used") . ' (' . $license_pd . ')', self::QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS * min($license_pd, self::THRESHOLD_MANY_IMAGES), ); } if ($no_license_or_rightsholder) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageLicenses, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_rightsholder_no_rightsstatus") . ' (' . $no_license_or_rightsholder . ')', self::QI_PENALTY_IMAGE_NO_OWNER_NO_LICENCE * min($no_license_or_rightsholder, self::THRESHOLD_MANY_IMAGES), ); } if ($no_rightsholder) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageLicenses, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_rightsholder") . ' (' . $no_rightsholder . ')', self::QI_PENALTY_IMAGE_NO_OWNER * $no_rightsholder, min(self::THRESHOLD_MANY_IMAGES), ); } if ($no_license) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageLicenses, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "no_rightsstatus") . ' (' . $no_license . ')', self::QI_PENALTY_IMAGE_NO_LICENCE * min($no_license, self::THRESHOLD_MANY_IMAGES), ); } if ($license_open_access_attribution) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageLicenses, MDPuqiMessageStatus::praise, $this->_tlLoader->tl("quality", "quality", "open_access_licence_used") . ' (' . $license_open_access_attribution . ')', self::QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS_ATTRIBUTION * min($license_open_access_attribution, self::THRESHOLD_MANY_IMAGES), ); } if ($license_closed_access) { $this->_messages[] = new MDPuqiMessage( MDPuqiCheckSection::imageLicenses, MDPuqiMessageStatus::warning, $this->_tlLoader->tl("quality", "quality", "restrictive_licence_used") . ' (' . $license_closed_access . ')', self::QI_PENALTY_IMAGE_LICENCE_CLOSED_ACCESS * min($license_closed_access, self::THRESHOLD_MANY_IMAGES), ); } } /** * Marks a section checked for completeness validation (e.g. to ignore it). * * @param MDPuqiCheckSection $section Section to mark checked. * * @return void */ public function markSectionChecked(MDPuqiCheckSection $section):void { $this->_checkedSections[] = $section; } /** * Returns true if all fields have been checked. * * @return void */ public function validateChecksCompleteness():void { foreach (MDPuqiCheckSection::cases() as $case) { if (!in_array($case, $this->_checkedSections, true)) { throw new Exception("Section " . $case->name . " was not checked"); } } if (count(MDPuqiCheckSection::cases()) < count($this->_checkedSections)) { throw new Exception("A section has been checked twice"); } } /** * Returns warnings. * * @return array */ public function getMessages():array { return $this->_messages; } /** * Returns aggregate score. * * @return integer */ public function getScore():int { $output = 0; foreach ($this->_messages as $msg) { $output += $msg->score; } return $output; } /** * Constructor. * * @param MDTlLoader $tlLoader Translation loader. * * @return void */ public function __construct(MDTlLoader $tlLoader) { $this->_tlLoader = $tlLoader; } }