diff --git a/src/MDPuqi.php b/src/MDPuqi.php index aa6570e..5f4f6d6 100644 --- a/src/MDPuqi.php +++ b/src/MDPuqi.php @@ -13,4 +13,1127 @@ declare(strict_types = 1); */ final class MDPuqi { + // 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; + + const THRESHOLD_TAGS_TOO_MANY = 10; + const THRESHOLD_IMG_TOO_SMALL = 600; // px + const THRESHOLD_IMG_SMALL = 800; // px + const THRESHOLD_IMG_GOOD_SIZE_SHORTER = 1200; // px + const THRESHOLD_IMG_GOOD_SIZE_LONGER = 1920; // px + + const LICENSES_OPEN_ACCESS_PD = ["CC0", "Public Domain Mark", "Orphan Work"]; + const LICENSES_OPEN_ACCESS_ATTRIBUTION = ["CC BY"]; + const LICENSES_CLOSED_ACCESS = ["RR-F", "RR-P", "RR-R"]; + + const QI_REWARD_DESCRIPTION_LENGTH_GOOD = 3; + const QI_REWARD_DESCRIPTION_LENGTH_GREAT = 6; + const QI_REWARD_EVENTS_MULTIPLIER = 5; + const QI_REWARD_TAGS_MULTIPLIER = 3; + const QI_REWARD_LITERATURE_MULTIPLIER = 2; + const QI_REWARD_HYPERLINKS_MULTIPLIER = 2; + const QI_REWARD_DOCUMENTS_MULTIPLIER = 1; + const QI_REWARD_LINKED_OBJS_MULTIPLIER = 3; + const QI_REWARD_SERIES_MULTIPLIER = 4; + const QI_REWARD_EXHIBITIONS_MULTIPLIER = 4; + const QI_REWARD_TRANSLATIONS_MULTIPLIER = 10; + const QI_REWARD_TRANSCRIPTS_MULTIPLIER = 10; + const QI_REWARD_OBJ_IS_REF_MULTIPLIER = 5; + const QI_REWARD_OBJ_RECEPTION_MULTIPLIER = 1; + const QI_REWARD_MARKINGS_MULTIPLIER = 3; + const QI_REWARD_IMGS_RESOURCES_MULTIPLIER = 3; + const QI_REWARD_IMAGE_SIZE_LARGE = 1; + const QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS = 2; + const QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS_ATTRIBUTION = 1; + const QI_REWARD_METADATA_LICENCE_OPEN_ACCESS = 5; + const QI_REWARD_METADATA_LICENCE_OPEN_ACCESS_ATTRIBUTION = 3; + + const QI_REWARD_PUBLIC_INSCRIPTION = 3; + const QI_REWARD_PUBLIC_COMPARABLE_OBJECTS = 3; + const QI_REWARD_PUBLIC_DETAILED_DESCRIPTION = 5; + + const QI_PENALTY_EMPTY_OBJECT_TYPE = -20; + const QI_PENALTY_TITLE_ONE_WORD = -3; + const QI_PENALTY_TITLE_DUPLICATE = -5; + const QI_PENALTY_TITLE_TOO_LONG = -5; + const QI_PENALTY_DESCRIPTION_DUPLICATE = -25; + const QI_PENALTY_DESCRIPTION_TOO_SHORT = -10; + const QI_PENALTY_DESCRIPTION_SHORT = -5; + const QI_PENALTY_DESCRIPTION_TOO_LONG = -6; + const QI_PENALTY_EMPTY_MATTECH = -3; + const QI_PENALTY_EMPTY_MEASUREMENTS = -5; + + const QI_PENALTY_NO_IMAGE = -10; + const QI_PENALTY_NO_COLLECTION = -10; + const QI_PENALTY_MULTIPLIER_EVENT_PART_DOUBLE_LINKED_AS_RELATED = -10; + const QI_PENALTY_NO_EVENTS = -15; + const QI_PENALTY_NO_TAG = -15; + const QI_PENALTY_ONLY_ONE_TAG = -10; + const QI_PENALTY_ONLY_TWO_TAG = -5; + const QI_PENALTY_TOO_MANY_TAGS = -3; // Per tag of more than 10 tags + + const QI_PENALTY_IMAGE_FILE_MISSING = -5; + const QI_PENALTY_IMAGE_TOO_SMALL = -5; + const QI_PENALTY_IMAGE_SMALL = -2; + const QI_PENALTY_IMAGE_NO_OWNER_NO_LICENCE = -15; + const QI_PENALTY_IMAGE_NO_OWNER = -10; + const QI_PENALTY_IMAGE_NO_LICENCE = -10; + const QI_PENALTY_IMAGE_LICENCE_CLOSED_ACCESS = -1; + const QI_PENALTY_METADATA_NO_LICENCE = -10; + 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 = []; + + /** + * 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 = 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 (count_words($object_type) > self::THRESHOLD_WC_OBJECT_TYPE_TOO_LONG) { + + $this->_messages[] = new MDPuqiMessage( + MDPuqiCheckSection::objectType, + MDPuqiMessageStatus::neutral, + 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", "too_many__tags"); + $status = MDPuqiMessageStatus::warning; + $value = self::QI_PENALTY_TOO_MANY_TAGS * ($anzahl_swoergebnis - 10); + } + + $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_OBJ_IS_REF_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(10, $count) - 1), + ); + 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::warning, + $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::warning, + $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 * $imgsTooSmall, + ); + + } + 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 * $imgsSmall, + ); + + } + 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 * $imgsLarge, + ); + + } + + } + + /** + * 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'])) { + $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 * $license_pd, + ); + } + 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 * $no_license_or_rightsholder, + ); + } + 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, + ); + } + 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 * $no_license, + ); + } + 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 * $license_open_access_attribution, + ); + } + 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 * $license_closed_access, + ); + } + + } + + /** + * 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 { + + if (array_unique($this->_checkedSections) !== $this->_checkedSections) { + throw new Exception("A section has been checked twice"); + } + + foreach (MDPuqiCheckSection::cases() as $case) { + if (!in_array($case, $this->_checkedSections, true)) { + throw new Exception("Section " . $case->name . " was not checked"); + } + } + + } + + /** + * 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; + + } } diff --git a/src/MDPuqiCheckSection.php b/src/MDPuqiCheckSection.php new file mode 100644 index 0000000..93b2f4b --- /dev/null +++ b/src/MDPuqiCheckSection.php @@ -0,0 +1,70 @@ + + */ +declare(strict_types = 1); + +/** + * Represents a section of object data that is checked by Puqi. + */ +enum MDPuqiCheckSection implements JsonSerializable { + + case objectName; + case objectType; + case objectDescription; + case objectMaterialTechnique; + case objectMeasurements; + case events; + case tags; + case collectionCount; + case literatureCount; + case hyperlinkCount; + case documentCount; + case linkedObjectCount; + case seriesCount; + case exhibitionCount; + case translationCount; + case transcriptCount; + case objectHasReceptionCount; + case objectIsReferenceCount; + case markingCount; + case imageCount; + case publicInscription; + case publicDetailedDescription; + case publicComparableObjects; + case metadataLicense; + case imageMissing; + case imageSizes; + case imageLicenses; + + /** + * Lists all available names. + * + * @return array + */ + public static function caseNames():array { + + $output = []; + + $cases = self::cases(); + foreach ($cases as $case) { + $output[] = $case->name; + } + + return $output; + + } + + /** + * Provides the option to serialize as a string during json_encode(). + * + * @return string + */ + public function jsonSerialize():string { + + return $this->name; + + } +} diff --git a/src/MDPuqiMessage.php b/src/MDPuqiMessage.php new file mode 100644 index 0000000..417badd --- /dev/null +++ b/src/MDPuqiMessage.php @@ -0,0 +1,53 @@ + + */ +declare(strict_types = 1); + +/** + * Describes a PuQI message. + */ +final class MDPuqiMessage implements JsonSerializable { + + public readonly MDPuqiCheckSection $section; // Section of object data that is described by the message. + public readonly MDPuqiMessageStatus $status; + public readonly string $message; // Text message. + public readonly int $score; + + /** + * Provides the option to serialize as a string during json_encode(). + * + * @return array{section: MDPuqiCheckSection, status: MDPuqiMessageStatus, message: string, score: integer} + */ + public function jsonSerialize():array { + + return [ + 'section' => $this->section, + 'status' => $this->status, + 'message' => $this->message, + 'score' => $this->score + ]; + + } + + /** + * Constructor. + * + * @param MDPuqiCheckSection $section Section described by this message. + * @param MDPuqiMessageStatus $status Status. + * @param string $message Message. + * @param integer $score Score impact of the message. + * + * @return void + */ + public function __construct(MDPuqiCheckSection $section, MDPuqiMessageStatus $status, string $message, int $score) { + + $this->section = $section; + $this->status = $status; + $this->message = $message; + $this->score = $score; + + } +} diff --git a/src/MDPuqiMessageStatus.php b/src/MDPuqiMessageStatus.php new file mode 100644 index 0000000..4c1fc99 --- /dev/null +++ b/src/MDPuqiMessageStatus.php @@ -0,0 +1,46 @@ + + */ +declare(strict_types = 1); + +/** + * Represents a warning / praise status of a message. + */ +enum MDPuqiMessageStatus implements JsonSerializable { + + case warning; + case neutral; + case praise; + + /** + * Lists all available names. + * + * @return array + */ + public static function caseNames():array { + + $output = []; + + $cases = self::cases(); + foreach ($cases as $case) { + $output[] = $case->name; + } + + return $output; + + } + + /** + * Provides the option to serialize as a string during json_encode(). + * + * @return string + */ + public function jsonSerialize():string { + + return $this->name; + + } +}