Compare commits

...

11 Commits

12 changed files with 555 additions and 98 deletions

@@ -5,7 +5,7 @@ records and provide tips for improving it.
## Tools ## Tools
- PuQI ### PuQI
PuQI (class `MDPuqi`) generates a score determining the quality of object metadata PuQI (class `MDPuqi`) generates a score determining the quality of object metadata
based on e.g. counting linked entries, checking the length of an object's description, based on e.g. counting linked entries, checking the length of an object's description,
@@ -42,6 +42,12 @@ such a creator can be identified among the object's creators, the returned warni
contain an additional notice about the creator being represented by one along with tips contain an additional notice about the creator being represented by one along with tips
on how to get in contact with the copyright collective. on how to get in contact with the copyright collective.
### Additional Tools
#### Minimaldatensatz: Check for Availability of Fields Required and Recommended by the AG Minimaldatensatz
See <http://minimaldatensatz.de>
## Publications ## Publications
### PuQI ### PuQI

17
phpunit.xml Normal file

@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" backupGlobals="false" beStrictAboutChangesToGlobalState="false" beStrictAboutOutputDuringTests="false" bootstrap="tests/bootstrap.php" cacheResult="false" colors="true" enforceTimeLimit="true" failOnWarning="true" processIsolation="true" stopOnError="true" stopOnFailure="true" stopOnIncomplete="true" stopOnSkipped="true" stopOnRisky="true" stopOnWarning="true" timeoutForSmallTests="1" timeoutForMediumTests="10" timeoutForLargeTests="60" cacheDirectory=".phpunit.cache" backupStaticProperties="false" requireCoverageMetadata="false" beStrictAboutCoverageMetadata="false"
displayDetailsOnIncompleteTests="true"
displayDetailsOnSkippedTests="true"
displayDetailsOnTestsThatTriggerDeprecations="true"
displayDetailsOnTestsThatTriggerErrors="true"
displayDetailsOnTestsThatTriggerNotices="true"
displayDetailsOnTestsThatTriggerWarnings="true">
<testsuite name="tests">
<directory>tests</directory>
</testsuite>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>

@@ -0,0 +1,133 @@
<?PHP
/**
* Since about 2022, a Germany-wide working group got together to work on a minimal
* set of recommended and required fields for a basic object record. Primarily, this
* targets import data for the German Digital Library.
* This check is German-language only.
*
* @see http://minimaldatensatz.de
*
* @author Joshua Ramon Enslin <joshua@museum-digital.de>
*/
declare(strict_types = 1);
/**
* Since about 2022, a Germany-wide working group got together to work on a minimal
* set of recommended and required fields for a basic object record. Primarily, this
* targets import data for the German Digital Library.
* This check is German-language only.
*/
final class MinimaldatensatzCheck {
public bool $has_title;
public bool $has_type;
public bool $has_topic_category;
public bool $has_inventory_number;
public bool $has_description;
public bool $has_material;
public bool $has_technique;
public bool $has_measurements;
public bool $has_event;
public bool $has_tag;
public bool $has_image;
public bool $has_image_license;
public bool $has_image_owner;
/**
* Returns an evaluation message for a given required field.
*
* @param string $field Field to evaluate.
* @param boolean $passed Sets whether the field is available and filled or not.
* @param string $fieldname_de German language field name.
*
* @return array{field: string, required: true, passed: bool, text: string}
*/
private function _generateOutputMessageForRequiredField(string $field, bool $passed, string $fieldname_de):array {
return [
'field' => $field,
'required' => true,
'passed' => $passed,
'text' => match($passed) {
true => 'Das Feld "' . $fieldname_de . '" ist vorhanden und ausgefüllt. Gut.',
false => 'Das Pflichtfeld "' . $fieldname_de . '" ist nicht vorhanden oder nicht ausgefüllt.',
},
];
}
/**
* Returns an evaluation message for a given optional field.
*
* @param string $field Field to evaluate.
* @param boolean $passed Sets whether the field is available and filled or not.
* @param string $fieldname_de German language field name.
*
* @return array{field: string, required: false, passed: bool, text: string}
*/
private function _generateOutputMessageForOptionalField(string $field, bool $passed, string $fieldname_de):array {
return [
'field' => $field,
'required' => false,
'passed' => $passed,
'text' => match($passed) {
true => 'Das Feld "' . $fieldname_de . '" ist vorhanden und ausgefüllt. Gut.',
false => 'Das Feld "' . $fieldname_de . '" ist nicht vorhanden oder nicht ausgefüllt. Es wird empfolen, diese Information mitzuliefern.',
},
];
}
/**
* Returns a list of evaluations / checks for each field.
*
* @return list<array{field: string, required: bool, passed: bool, text: string}>
*/
public function evaluate():array {
$output = [];
if (!isset($this->has_title)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Title has not been checked");
$output[] = $this->_generateOutputMessageForRequiredField("title", $this->has_title, "Objekttitel oder -benennung");
if (!isset($this->has_type)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Type has not been checked");
$output[] = $this->_generateOutputMessageForRequiredField("type", $this->has_type, "Objekttyp oder -bezeichnung");
if (!isset($this->has_topic_category)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Topic category has not been checked");
$output[] = $this->_generateOutputMessageForOptionalField("topic_category", $this->has_topic_category, "Themenkategorie");
if (!isset($this->has_inventory_number)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Inventory number has not been checked");
$output[] = $this->_generateOutputMessageForRequiredField("inventory_number", $this->has_inventory_number, "Inventarnummer");
if (!isset($this->has_description)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Description has not been checked");
$output[] = $this->_generateOutputMessageForOptionalField("description", $this->has_description, "Objektbeschreibung");
if (!isset($this->has_material)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Material has not been checked");
$output[] = $this->_generateOutputMessageForOptionalField("material", $this->has_material, "Material");
if (!isset($this->has_technique)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Technique has not been checked");
$output[] = $this->_generateOutputMessageForOptionalField("technique", $this->has_technique, "Technik");
if (!isset($this->has_measurements)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Measurements has not been checked");
$output[] = $this->_generateOutputMessageForOptionalField("measurements", $this->has_measurements, "Maße");
if (!isset($this->has_event)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Event has not been checked");
$output[] = $this->_generateOutputMessageForRequiredField("event", $this->has_event, "Ereignis in der Objektgeschichte (Feldgruppe)");
if (!isset($this->has_tag)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Tag has not been checked");
$output[] = $this->_generateOutputMessageForOptionalField("tag", $this->has_tag, "Inhaltsschlagwort");
if (!isset($this->has_image)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Image has not been checked");
$output[] = $this->_generateOutputMessageForRequiredField("image", $this->has_image, "Mediendatei (Feldgruppe)");
if (!isset($this->has_image_license)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Image license has not been checked");
$output[] = $this->_generateOutputMessageForRequiredField("image_license", $this->has_image_license, "Nutzungsrechte Mediendatei");
if (!isset($this->has_image_owner)) throw new MinimaldatensatzCheckIncompletelyImplementedException("Image owner has not been checked");
$output[] = $this->_generateOutputMessageForRequiredField("image_owner", $this->has_image_owner, "Rechteinhaber*in Mediendatei");
return $output;
}
}

@@ -0,0 +1,25 @@
<?PHP
/**
* Custom exception class to be raised when not all fields have been set in the MinimaldatensatzCheck class.
*
* @file
*
* @author Joshua Ramon Enslin <joshua@museum-digital.de>
*/
declare(strict_types = 1);
/**
* Custom exception class to be raised when not all fields have been set in the MinimaldatensatzCheck class.
*/
final class MinimaldatensatzCheckIncompletelyImplementedException extends Exception {
/**
* Error message.
*
* @return string
*/
public function errorMessage() {
//error message
return 'Incompletely implemented check for Minimaldatensatz: ' . $this->getMessage();
}
}

@@ -0,0 +1,3 @@
# Checks for required and recommended fields according to the work of the Working Group "Minimaldatensatz" (Minimal Object Record)
See: http://minimaldatensatz.de

@@ -265,13 +265,13 @@ final class MDPlausi {
*/ */
private function _evaluateProductionAfterPostproduction():void { private function _evaluateProductionAfterPostproduction():void {
if ($this->_post_production->earliest_event_type !== 0 if ($this->_post_production->latest_event_type !== 0
&& $this->_production->earliest_event_type !== 0 && $this->_production->earliest_event_type !== 0
&& $this->_post_production->earliest < $this->_production->earliest) { && $this->_post_production->latest < $this->_production->earliest) {
$message = $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_post_production->earliest_event_type); $message = $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_post_production->latest_event_type);
$message .= $this->_post_production->formulateMessagePartForEarliest($this->_tlLoader); $message .= $this->_post_production->formulateMessagePartForLatest($this->_tlLoader);
$message .= ' ' . strtolower($this->_tlLoader->tl("quality", "quality", "before")) . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->earliest_event_type); $message .= ' ' . strtolower($this->_tlLoader->tl("quality", "quality", "before")) . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->earliest_event_type);
@@ -282,6 +282,22 @@ final class MDPlausi {
} }
else if ($this->_post_production->earliest_event_type !== 0
&& $this->_production->earliest_event_type !== 0
&& $this->_post_production->earliest < $this->_production->earliest) {
$message = $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_post_production->earliest_event_type);
$message .= $this->_post_production->formulateMessagePartForEarliest($this->_tlLoader);
$message .= ' ' . strtolower($this->_tlLoader->tl("quality", "quality", "before_possibly")) . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->earliest_event_type);
$message .= $this->_production->formulateMessagePartForEarliest($this->_tlLoader);
$this->_messageList[] = $message;
}
else if ($this->_post_production->earliest_event_type !== 0 else if ($this->_post_production->earliest_event_type !== 0
&& $this->_production->latest_event_type !== 0 && $this->_production->latest_event_type !== 0
&& $this->_post_production->earliest < $this->_production->latest) { && $this->_post_production->earliest < $this->_production->latest) {

@@ -37,8 +37,8 @@ final class MDPlausiEvent {
if (empty($time)) return ['earliest' => false, 'latest' => false]; if (empty($time)) return ['earliest' => false, 'latest' => false];
// 2005 // 20, -1, 233, -233, 2005, -2005
if (in_array(strlen($time), [4, 5], true) and is_numeric(substr($time, 1))) { if (in_array(strlen($time), [2, 3, 4, 5], true) and is_numeric(substr($time, 1))) {
return [ return [
'earliest' => strtotime($time . '-01-01'), 'earliest' => strtotime($time . '-01-01'),
'latest' => strtotime($time . '-12-31'), 'latest' => strtotime($time . '-12-31'),

@@ -30,71 +30,72 @@ final class MDPuqi {
private const THRESHOLD_CC_OBJECT_DESC_GREAT = 1750; private const THRESHOLD_CC_OBJECT_DESC_GREAT = 1750;
private const THRESHOLD_CC_OBJECT_DESC_QUITE_LONG = 5000; private const THRESHOLD_CC_OBJECT_DESC_QUITE_LONG = 5000;
const THRESHOLD_TAGS_TOO_MANY = 10; public const THRESHOLD_TAGS_TOO_MANY = 10;
const THRESHOLD_IMG_TOO_SMALL = 600; // px public const THRESHOLD_MANY_IMAGES = 10;
const THRESHOLD_IMG_SMALL = 800; // px public const THRESHOLD_IMG_TOO_SMALL = 600; // px
const THRESHOLD_IMG_GOOD_SIZE_SHORTER = 1200; // px public const THRESHOLD_IMG_SMALL = 800; // px
const THRESHOLD_IMG_GOOD_SIZE_LONGER = 1920; // px public const THRESHOLD_IMG_GOOD_SIZE_SHORTER = 1200; // px
public const THRESHOLD_IMG_GOOD_SIZE_LONGER = 1920; // px
const LICENSES_OPEN_ACCESS_PD = ["CC0", "Public Domain Mark", "Orphan Work"]; public const LICENSES_OPEN_ACCESS_PD = ["CC0", "Public Domain Mark", "Orphan Work"];
const LICENSES_OPEN_ACCESS_ATTRIBUTION = ["CC BY"]; public const LICENSES_OPEN_ACCESS_ATTRIBUTION = ["CC BY"];
const LICENSES_CLOSED_ACCESS = ["RR-F", "RR-P", "RR-R"]; public const LICENSES_CLOSED_ACCESS = ["RR-F", "RR-P", "RR-R"];
const QI_REWARD_DESCRIPTION_LENGTH_GOOD = 3; public const QI_REWARD_DESCRIPTION_LENGTH_GOOD = 3;
const QI_REWARD_DESCRIPTION_LENGTH_GREAT = 6; private const QI_REWARD_DESCRIPTION_LENGTH_GREAT = 6;
const QI_REWARD_EVENTS_MULTIPLIER = 5; private const QI_REWARD_EVENTS_MULTIPLIER = 5;
const QI_REWARD_TAGS_MULTIPLIER = 3; public const QI_REWARD_TAGS_MULTIPLIER = 3;
const QI_REWARD_LITERATURE_MULTIPLIER = 2; public const QI_REWARD_TAGS_MULTIPLIER_ABOVE_THRESHOLD = 1;
const QI_REWARD_HYPERLINKS_MULTIPLIER = 2; private const QI_REWARD_LITERATURE_MULTIPLIER = 2;
const QI_REWARD_DOCUMENTS_MULTIPLIER = 1; private const QI_REWARD_HYPERLINKS_MULTIPLIER = 2;
const QI_REWARD_LINKED_OBJS_MULTIPLIER = 3; private const QI_REWARD_DOCUMENTS_MULTIPLIER = 1;
const QI_REWARD_SERIES_MULTIPLIER = 4; private const QI_REWARD_LINKED_OBJS_MULTIPLIER = 3;
const QI_REWARD_EXHIBITIONS_MULTIPLIER = 4; private const QI_REWARD_SERIES_MULTIPLIER = 4;
const QI_REWARD_TRANSLATIONS_MULTIPLIER = 10; private const QI_REWARD_EXHIBITIONS_MULTIPLIER = 4;
const QI_REWARD_TRANSCRIPTS_MULTIPLIER = 10; private const QI_REWARD_TRANSLATIONS_MULTIPLIER = 10;
const QI_REWARD_OBJ_IS_REF_MULTIPLIER = 5; private const QI_REWARD_TRANSCRIPTS_MULTIPLIER = 10;
const QI_REWARD_OBJ_RECEPTION_MULTIPLIER = 1; private const QI_REWARD_OBJ_IS_REF_MULTIPLIER = 5;
const QI_REWARD_MARKINGS_MULTIPLIER = 3; private const QI_REWARD_OBJ_RECEPTION_MULTIPLIER = 1;
const QI_REWARD_IMGS_RESOURCES_MULTIPLIER = 3; private const QI_REWARD_MARKINGS_MULTIPLIER = 3;
const QI_REWARD_IMAGE_SIZE_LARGE = 1; public const QI_REWARD_IMGS_RESOURCES_MULTIPLIER = 3;
const QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS = 2; public const QI_REWARD_IMAGE_SIZE_LARGE = 1;
const QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS_ATTRIBUTION = 1; private const QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS = 2;
const QI_REWARD_METADATA_LICENCE_OPEN_ACCESS = 5; private const QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS_ATTRIBUTION = 1;
const QI_REWARD_METADATA_LICENCE_OPEN_ACCESS_ATTRIBUTION = 3; private const QI_REWARD_METADATA_LICENCE_OPEN_ACCESS = 5;
private const QI_REWARD_METADATA_LICENCE_OPEN_ACCESS_ATTRIBUTION = 3;
const QI_REWARD_PUBLIC_INSCRIPTION = 3; private const QI_REWARD_PUBLIC_INSCRIPTION = 3;
const QI_REWARD_PUBLIC_COMPARABLE_OBJECTS = 3; private const QI_REWARD_PUBLIC_COMPARABLE_OBJECTS = 3;
const QI_REWARD_PUBLIC_DETAILED_DESCRIPTION = 5; private const QI_REWARD_PUBLIC_DETAILED_DESCRIPTION = 5;
const QI_PENALTY_EMPTY_OBJECT_TYPE = -20; private const QI_PENALTY_EMPTY_OBJECT_TYPE = -20;
const QI_PENALTY_TITLE_ONE_WORD = -3; private const QI_PENALTY_TITLE_ONE_WORD = -3;
const QI_PENALTY_TITLE_DUPLICATE = -5; private const QI_PENALTY_TITLE_DUPLICATE = -5;
const QI_PENALTY_TITLE_TOO_LONG = -5; private const QI_PENALTY_TITLE_TOO_LONG = -5;
const QI_PENALTY_DESCRIPTION_DUPLICATE = -25; private const QI_PENALTY_DESCRIPTION_DUPLICATE = -25;
const QI_PENALTY_DESCRIPTION_TOO_SHORT = -10; private const QI_PENALTY_DESCRIPTION_TOO_SHORT = -10;
const QI_PENALTY_DESCRIPTION_SHORT = -5; private const QI_PENALTY_DESCRIPTION_SHORT = -5;
const QI_PENALTY_DESCRIPTION_TOO_LONG = -6; private const QI_PENALTY_DESCRIPTION_TOO_LONG = -6;
const QI_PENALTY_EMPTY_MATTECH = -3; private const QI_PENALTY_EMPTY_MATTECH = -3;
const QI_PENALTY_EMPTY_MEASUREMENTS = -5; private const QI_PENALTY_EMPTY_MEASUREMENTS = -5;
const QI_PENALTY_NO_IMAGE = -10; private const QI_PENALTY_NO_IMAGE = -10;
const QI_PENALTY_NO_COLLECTION = -10; private const QI_PENALTY_NO_COLLECTION = -10;
const QI_PENALTY_MULTIPLIER_EVENT_PART_DOUBLE_LINKED_AS_RELATED = -10; private const QI_PENALTY_MULTIPLIER_EVENT_PART_DOUBLE_LINKED_AS_RELATED = -10;
const QI_PENALTY_NO_EVENTS = -15; public const QI_PENALTY_NO_EVENTS = -15;
const QI_PENALTY_NO_TAG = -15; public const QI_PENALTY_NO_TAG = -15;
const QI_PENALTY_ONLY_ONE_TAG = -10; public const QI_PENALTY_ONLY_ONE_TAG = -10;
const QI_PENALTY_ONLY_TWO_TAG = -5; public 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; private const QI_PENALTY_IMAGE_FILE_MISSING = -5;
const QI_PENALTY_IMAGE_TOO_SMALL = -5; public const QI_PENALTY_IMAGE_TOO_SMALL = -5;
const QI_PENALTY_IMAGE_SMALL = -2; public const QI_PENALTY_IMAGE_SMALL = -2;
const QI_PENALTY_IMAGE_NO_OWNER_NO_LICENCE = -15; private const QI_PENALTY_IMAGE_NO_OWNER_NO_LICENCE = -15;
const QI_PENALTY_IMAGE_NO_OWNER = -10; private const QI_PENALTY_IMAGE_NO_OWNER = -10;
const QI_PENALTY_IMAGE_NO_LICENCE = -10; private const QI_PENALTY_IMAGE_NO_LICENCE = -10;
const QI_PENALTY_IMAGE_LICENCE_CLOSED_ACCESS = -1; private const QI_PENALTY_IMAGE_LICENCE_CLOSED_ACCESS = -1;
const QI_PENALTY_METADATA_NO_LICENCE = -10; private const QI_PENALTY_METADATA_NO_LICENCE = -10;
const QI_PENALTY_METADATA_LICENCE_CLOSED_ACCESS = -3; private const QI_PENALTY_METADATA_LICENCE_CLOSED_ACCESS = -3;
public const INDICATOR_THRESHOLD_COLOR_RED = -30; public const INDICATOR_THRESHOLD_COLOR_RED = -30;
public const INDICATOR_THRESHOLD_COLOR_BLUE = 10; public const INDICATOR_THRESHOLD_COLOR_BLUE = 10;
@@ -445,9 +446,9 @@ final class MDPuqi {
$value = self::QI_REWARD_TAGS_MULTIPLIER * $anzahl_swoergebnis; $value = self::QI_REWARD_TAGS_MULTIPLIER * $anzahl_swoergebnis;
} }
else { else {
$msg = $this->_tlLoader->tl("quality", "quality", "too_many__tags"); $msg = $this->_tlLoader->tl("quality", "quality", "tree_nine__tags");
$status = MDPuqiMessageStatus::warning; $status = MDPuqiMessageStatus::praise;
$value = self::QI_PENALTY_TOO_MANY_TAGS * ($anzahl_swoergebnis - 10); $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( $this->_messages[] = new MDPuqiMessage(
@@ -740,7 +741,7 @@ final class MDPuqi {
MDPuqiCheckSection::markingCount, MDPuqiCheckSection::markingCount,
MDPuqiMessageStatus::praise, MDPuqiMessageStatus::praise,
$this->_tlLoader->tl("quality", "quality", "markings_exist"), $this->_tlLoader->tl("quality", "quality", "markings_exist"),
self::QI_REWARD_OBJ_IS_REF_MULTIPLIER * $count, self::QI_REWARD_MARKINGS_MULTIPLIER * $count,
); );
} }
@@ -780,7 +781,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageCount, MDPuqiCheckSection::imageCount,
MDPuqiMessageStatus::praise, MDPuqiMessageStatus::praise,
$this->_tlLoader->tl("quality", "quality", "many_images"), $this->_tlLoader->tl("quality", "quality", "many_images"),
self::QI_REWARD_IMGS_RESOURCES_MULTIPLIER * (min(10, $count) - 1), self::QI_REWARD_IMGS_RESOURCES_MULTIPLIER * min(self::THRESHOLD_MANY_IMAGES, $count),
); );
break; break;
} }
@@ -894,7 +895,7 @@ final class MDPuqi {
$this->_messages[] = new MDPuqiMessage( $this->_messages[] = new MDPuqiMessage(
MDPuqiCheckSection::metadataLicense, MDPuqiCheckSection::metadataLicense,
MDPuqiMessageStatus::warning, MDPuqiMessageStatus::praise,
$this->_tlLoader->tl("basis", "basis", "metadata") . ': ' . $this->_tlLoader->tl("quality", "quality", "open_access_licence_used"), $this->_tlLoader->tl("basis", "basis", "metadata") . ': ' . $this->_tlLoader->tl("quality", "quality", "open_access_licence_used"),
self::QI_REWARD_METADATA_LICENCE_OPEN_ACCESS_ATTRIBUTION, self::QI_REWARD_METADATA_LICENCE_OPEN_ACCESS_ATTRIBUTION,
); );
@@ -979,7 +980,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageSizes, MDPuqiCheckSection::imageSizes,
MDPuqiMessageStatus::warning, MDPuqiMessageStatus::warning,
$this->_tlLoader->tl("quality", "quality", "image_too_small") . ' (' . $imgsTooSmall . ')', $this->_tlLoader->tl("quality", "quality", "image_too_small") . ' (' . $imgsTooSmall . ')',
self::QI_PENALTY_IMAGE_TOO_SMALL * $imgsTooSmall, self::QI_PENALTY_IMAGE_TOO_SMALL * min($imgsTooSmall, self::THRESHOLD_MANY_IMAGES),
); );
} }
@@ -989,7 +990,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageSizes, MDPuqiCheckSection::imageSizes,
MDPuqiMessageStatus::warning, MDPuqiMessageStatus::warning,
$this->_tlLoader->tl("quality", "quality", "image_quite_small") . ' (' . $imgsSmall . ')', $this->_tlLoader->tl("quality", "quality", "image_quite_small") . ' (' . $imgsSmall . ')',
self::QI_PENALTY_IMAGE_SMALL * $imgsSmall, self::QI_PENALTY_IMAGE_SMALL * min($imgsSmall, self::THRESHOLD_MANY_IMAGES),
); );
} }
@@ -999,7 +1000,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageSizes, MDPuqiCheckSection::imageSizes,
MDPuqiMessageStatus::praise, MDPuqiMessageStatus::praise,
$this->_tlLoader->tl("quality", "quality", "image_has_good_size") . ' (' . $imgsLarge . ')', $this->_tlLoader->tl("quality", "quality", "image_has_good_size") . ' (' . $imgsLarge . ')',
self::QI_REWARD_IMAGE_SIZE_LARGE * $imgsLarge, self::QI_REWARD_IMAGE_SIZE_LARGE * min($imgsLarge, self::THRESHOLD_MANY_IMAGES),
); );
} }
@@ -1027,7 +1028,7 @@ final class MDPuqi {
if (empty($cur['rightsholder']) and empty($cur['license'])) { if (empty($cur['rightsholder']) and empty($cur['license'])) {
$no_license_or_rightsholder++; $no_license_or_rightsholder++;
} }
else if (empty($cur['rightsholder'])) { else if (empty($cur['rightsholder']) && $cur['license'] !== 'Public Domain Mark') {
$no_rightsholder++; $no_rightsholder++;
} }
else if (empty($cur['license'])) { else if (empty($cur['license'])) {
@@ -1050,7 +1051,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageLicenses, MDPuqiCheckSection::imageLicenses,
MDPuqiMessageStatus::praise, MDPuqiMessageStatus::praise,
$this->_tlLoader->tl("quality", "quality", "open_access_licence_used") . ' (' . $license_pd . ')', $this->_tlLoader->tl("quality", "quality", "open_access_licence_used") . ' (' . $license_pd . ')',
self::QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS * $license_pd, self::QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS * min($license_pd, self::THRESHOLD_MANY_IMAGES),
); );
} }
if ($no_license_or_rightsholder) { if ($no_license_or_rightsholder) {
@@ -1058,7 +1059,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageLicenses, MDPuqiCheckSection::imageLicenses,
MDPuqiMessageStatus::warning, MDPuqiMessageStatus::warning,
$this->_tlLoader->tl("quality", "quality", "no_rightsholder_no_rightsstatus") . ' (' . $no_license_or_rightsholder . ')', $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, self::QI_PENALTY_IMAGE_NO_OWNER_NO_LICENCE * min($no_license_or_rightsholder, self::THRESHOLD_MANY_IMAGES),
); );
} }
if ($no_rightsholder) { if ($no_rightsholder) {
@@ -1066,7 +1067,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageLicenses, MDPuqiCheckSection::imageLicenses,
MDPuqiMessageStatus::warning, MDPuqiMessageStatus::warning,
$this->_tlLoader->tl("quality", "quality", "no_rightsholder") . ' (' . $no_rightsholder . ')', $this->_tlLoader->tl("quality", "quality", "no_rightsholder") . ' (' . $no_rightsholder . ')',
self::QI_PENALTY_IMAGE_NO_OWNER * $no_rightsholder, self::QI_PENALTY_IMAGE_NO_OWNER * min($no_rightsholder, self::THRESHOLD_MANY_IMAGES),
); );
} }
if ($no_license) { if ($no_license) {
@@ -1074,7 +1075,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageLicenses, MDPuqiCheckSection::imageLicenses,
MDPuqiMessageStatus::warning, MDPuqiMessageStatus::warning,
$this->_tlLoader->tl("quality", "quality", "no_rightsstatus") . ' (' . $no_license . ')', $this->_tlLoader->tl("quality", "quality", "no_rightsstatus") . ' (' . $no_license . ')',
self::QI_PENALTY_IMAGE_NO_LICENCE * $no_license, self::QI_PENALTY_IMAGE_NO_LICENCE * min($no_license, self::THRESHOLD_MANY_IMAGES),
); );
} }
if ($license_open_access_attribution) { if ($license_open_access_attribution) {
@@ -1082,7 +1083,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageLicenses, MDPuqiCheckSection::imageLicenses,
MDPuqiMessageStatus::praise, MDPuqiMessageStatus::praise,
$this->_tlLoader->tl("quality", "quality", "open_access_licence_used") . ' (' . $license_open_access_attribution . ')', $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, self::QI_REWARD_IMAGE_LICENCE_OPEN_ACCESS_ATTRIBUTION * min($license_open_access_attribution, self::THRESHOLD_MANY_IMAGES),
); );
} }
if ($license_closed_access) { if ($license_closed_access) {
@@ -1090,7 +1091,7 @@ final class MDPuqi {
MDPuqiCheckSection::imageLicenses, MDPuqiCheckSection::imageLicenses,
MDPuqiMessageStatus::warning, MDPuqiMessageStatus::warning,
$this->_tlLoader->tl("quality", "quality", "restrictive_licence_used") . ' (' . $license_closed_access . ')', $this->_tlLoader->tl("quality", "quality", "restrictive_licence_used") . ' (' . $license_closed_access . ')',
self::QI_PENALTY_IMAGE_LICENCE_CLOSED_ACCESS * $license_closed_access, self::QI_PENALTY_IMAGE_LICENCE_CLOSED_ACCESS * min($license_closed_access, self::THRESHOLD_MANY_IMAGES),
); );
} }

@@ -7,22 +7,13 @@
declare(strict_types = 1); declare(strict_types = 1);
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
if (!defined('TL_FILE_DIRS')) {
define('TL_FILE_DIRS', [ define('TL_FILE_DIRS', [
__DIR__ . '/../../../l10n/musdb/', __DIR__ . '/../../../l10n/musdb/',
__DIR__ . '/../../../l10n/quality-web/', __DIR__ . '/../../../l10n/quality-web/',
__DIR__ . '/../../importer/dependencies/MDAllowedValueSets/l18n/', __DIR__ . '/../../importer/dependencies/MDAllowedValueSets/l18n/',
]); ]);
}
require_once __DIR__ . '/../src/Checks/Plausi/MDPlausi.php';
require_once __DIR__ . '/../src/Checks/Plausi/MDEventCategory.php';
require_once __DIR__ . '/../src/Checks/Plausi/MDPlausiEventCategory.php';
require_once __DIR__ . '/../src/Checks/Plausi/MDPlausiEvent.php';
# require_once __DIR__ . '/../../MDTlLoader/src/MDTlLoader.php';
require_once __DIR__ . '/../../importer/dependencies/MDAllowedValueSets/src/MDRequirementsSet.php';
require_once __DIR__ . '/../../importer/dependencies/MDAllowedValueSets/src/MDValueSet.php';
require_once __DIR__ . '/../../importer/dependencies/MDAllowedValueSets/src/MDEventsSet.php';
require_once __DIR__ . '/../../importer/dependencies/MDTlLoader/src/MDTlLoader.php';
/** /**
* Tests for plausi. * Tests for plausi.

@@ -7,10 +7,196 @@
declare(strict_types = 1); declare(strict_types = 1);
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
/** /**
* Tests for PUQI. * Tests for PUQI.
*/ */
#[small]
#[CoversClass(\MDPuqi::class)]
final class MDPuqiTest extends TestCase { final class MDPuqiTest extends TestCase {
/**
* Provider for licenses.
*
* @return Generator<array{0: string, 1: MDPuqiMessageStatus, 2: bool}>
*/
public static function licenseProvider():Generator {
yield 'Empty' => ['', MDPuqiMessageStatus::warning, false];
yield 'Open' => ['CC0', MDPuqiMessageStatus::praise, true];
yield 'Open Attr' => ['CC BY', MDPuqiMessageStatus::praise, true];
yield 'Closed' => ['RR-F', MDPuqiMessageStatus::warning, false];
}
/**
* Provider for image sizes. 0: width, 1: height, 2: expected status, 3: expected score.
*
* @return Generator<array{0: int, 1: int, 2: MDPuqiMessageStatus, 3: int}>
*/
public static function imgSizesProvider():Generator {
yield 'Both too small' => [MDPuqi::THRESHOLD_IMG_TOO_SMALL - 1, MDPuqi::THRESHOLD_IMG_TOO_SMALL - 1, MDPuqiMessageStatus::warning, MDPuqi::QI_PENALTY_IMAGE_TOO_SMALL];
yield 'Width too small' => [MDPuqi::THRESHOLD_IMG_TOO_SMALL - 1, MDPuqi::THRESHOLD_IMG_TOO_SMALL, MDPuqiMessageStatus::warning, MDPuqi::QI_PENALTY_IMAGE_SMALL];
yield 'Height too small' => [MDPuqi::THRESHOLD_IMG_TOO_SMALL, MDPuqi::THRESHOLD_IMG_TOO_SMALL - 1, MDPuqiMessageStatus::warning, MDPuqi::QI_PENALTY_IMAGE_SMALL];
yield 'Both small' => [MDPuqi::THRESHOLD_IMG_SMALL - 1, MDPuqi::THRESHOLD_IMG_SMALL - 1, MDPuqiMessageStatus::warning, MDPuqi::QI_PENALTY_IMAGE_SMALL];
yield 'Both good sized' => [MDPuqi::THRESHOLD_IMG_GOOD_SIZE_SHORTER + 1, MDPuqi::THRESHOLD_IMG_GOOD_SIZE_LONGER + 1, MDPuqiMessageStatus::praise, MDPuqi::QI_REWARD_IMAGE_SIZE_LARGE];
}
/**
* Test for license.
*
* @param string $input Input string.
* @param MDPuqiMessageStatus $warningStatus Expected message status.
* @param boolean $positive Positive or negative result.
*
* @return void
*/
#[DataProvider('licenseProvider')]
public function testMetadataLicensesResultInExpectedAccessment(string $input, MDPuqiMessageStatus $warningStatus, bool $positive):void {
$tlLoader = new MDTlLoader("abc", "en");
$puqi = new MDPuqi($tlLoader);
$puqi->checkMetadataLicense($input);
$msgs = $puqi->getMessages();
self::assertEquals(1, count($msgs));
$msg = $msgs[0];
self::assertEquals($warningStatus, $msg->status);
$score = $puqi->getScore();
self::assertEquals($positive, $score > 0, "Expected score to be positive or negative, got the oppite result. Score is: " . $score);
}
/**
* Test for license.
*
* @param string $input Input string.
* @param MDPuqiMessageStatus $warningStatus Expected message status.
* @param boolean $positive Positive or negative result.
*
* @return void
*/
#[DataProvider('licenseProvider')]
public function testImageLicensesResultInExpectedAccessment(string $input, MDPuqiMessageStatus $warningStatus, bool $positive):void {
$puqi = new MDPuqi(new MDTlLoader("abc", "en"));
$puqi->checkImageLicenses([['license' => $input, 'rightsholder' => 'Somebody']]);
$msgs = $puqi->getMessages();
self::assertEquals(1, count($msgs));
$msg = $msgs[0];
self::assertEquals($warningStatus, $msg->status);
$score = $puqi->getScore();
self::assertEquals($positive, $score > 0, "Expected score to be positive or negative, got the oppite result. Score is: " . $score);
// Get score. Having the same 100 times should not inflate the score.
$expectedMaxScore = $score * MDPuqi::THRESHOLD_MANY_IMAGES;
$puqi = new MDPuqi(new MDTlLoader("abc", "en"));
$inp = [];
for ($i = 0; $i < MDPuqi::THRESHOLD_MANY_IMAGES * 10; $i++) {
$inp[] = ['license' => $input, 'rightsholder' => 'Somebody'];
}
$puqi->checkImageLicenses($inp);
self::assertEquals($expectedMaxScore, $puqi->getScore(), "Expected max. score to be stopped from increasing by threshold. It was not.");
}
/**
* Test for image sizes.
*
* @param integer $width Width.
* @param integer $height Height.
* @param MDPuqiMessageStatus $expectedStatus Expected message status.
* @param integer $expectedScore Expected score.
*
* @return void
*/
#[DataProvider('imgSizesProvider')]
public function testImageSizesResultInExpectedAccessment(int $width, int $height, MDPuqiMessageStatus $expectedStatus, int $expectedScore):void {
$puqi = new MDPuqi(new MDTlLoader("abc", "en"));
$puqi->checkImageSizes([['width' => $width, 'height' => $height]]);
$msgs = $puqi->getMessages();
self::assertEquals(1, count($msgs));
$msg = $msgs[0];
self::assertEquals($expectedStatus, $msg->status);
self::assertEquals($expectedScore, $puqi->getScore());
// Get score. Having the same 100 times should not inflate the score.
$expectedMaxScore = $expectedScore * MDPuqi::THRESHOLD_MANY_IMAGES;
$puqi = new MDPuqi(new MDTlLoader("abc", "en"));
$inp = [];
for ($i = 0; $i < MDPuqi::THRESHOLD_MANY_IMAGES * 10; $i++) {
$inp[] = ['width' => $width, 'height' => $height];
}
$puqi->checkImageSizes($inp);
self::assertEquals($expectedMaxScore, $puqi->getScore(), "Expected max. score to be stopped from increasing by threshold. It was not.");
}
/**
* Test that more than ten images do not result in inflated score.
* Case: checkImageCount.
*
* @return void
*/
public function testManyImagesDoesNotInflateScoreByCount():void {
$puqi = new MDPuqi(new MDTlLoader("abc", "en"));
$puqi->checkImageCount(500);
$msgs = $puqi->getMessages();
$score = $puqi->getScore();
self::assertEquals(MDPuqi::THRESHOLD_MANY_IMAGES * MDPuqi::QI_REWARD_IMGS_RESOURCES_MULTIPLIER, $score, "Image count eval: Score differs from expected. Messages: " . var_export($puqi->getMessages(), true));
}
/**
* Data provider for tag.
* 0: Input number of tags.
* 1: Expected score.
*
* @return Generator<array{0: int, 1: int}>
*/
public static function tagNoAndScoreProvider():Generator {
yield 'No tags' => [0, 0 + MDPuqi::QI_PENALTY_NO_TAG];
yield 'One tag' => [1, 0 + MDPuqi::QI_PENALTY_ONLY_ONE_TAG];
yield 'Two tag' => [2, 0 + MDPuqi::QI_PENALTY_ONLY_TWO_TAG];
yield 'Regular number of tags' => [5, 0 + 5 * MDPuqi::QI_REWARD_TAGS_MULTIPLIER];
yield 'Number of tags above threshold' => [MDPuqi::THRESHOLD_TAGS_TOO_MANY + 1, 0 + MDPuqi::THRESHOLD_TAGS_TOO_MANY * MDPuqi::QI_REWARD_TAGS_MULTIPLIER + 1 * MDPuqi::QI_REWARD_TAGS_MULTIPLIER_ABOVE_THRESHOLD];
}
/**
* Test for tag evaluation.
*
* @param integer $noOfTags Input number of tags.
* @param integer $expectedScore Expected score.
*
* @return void
*/
#[DataProvider('tagNoAndScoreProvider')]
public function testTagEvaluation(int $noOfTags, int $expectedScore):void {
$puqi = new MDPuqi(new MDTlLoader("abc", "en"));
$puqi->checkEvents([], [], [], [], $noOfTags);
$score = $puqi->getScore();
self::assertEquals(MDPuqi::QI_PENALTY_NO_EVENTS + $expectedScore, $score, "Tag eval: Score differs from expected. Messages: " . var_export($puqi->getMessages(), true));
}
} }

@@ -0,0 +1,31 @@
<?PHP
/**
* Tests for check for Minimaldatensatz conformity.
*
* @see http://minimaldatensatz.de
*
* @author Joshua Ramon Enslin <joshua@museum-digital.de>
*/
declare(strict_types = 1);
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../src/Checks/Minimaldatensatz/MinimaldatensatzCheck.php';
require_once __DIR__ . '/../src/Checks/Minimaldatensatz/MinimaldatensatzCheckIncompletelyImplementedException.php';
/**
* Tests for check for Minimaldatensatz conformity.
*/
final class MinimaldatensatzCheckTest extends TestCase {
/**
* Ensures that a warning is thrown, if a value has not been set.
*
* @return void
*/
public function testImageNotChecked():void {
$check = new MinimaldatensatzCheck;
$this->expectException(MinimaldatensatzCheckIncompletelyImplementedException::class);
$check->evaluate();
}
}

48
tests/bootstrap.php Normal file

@@ -0,0 +1,48 @@
<?PHP
/**
* Bootstraps tests by running provideEnv and then resetting the error handler.
*/
declare(strict_types = 1);
define('TL_FILE_DIRS', [
__DIR__ . '/../../../l10n/musdb/',
__DIR__ . '/../../../l10n/quality-web/',
__DIR__ . '/../../importer/dependencies/MDAllowedValueSets/l18n/',
]);
/**
* Autoloader for musdb.
*
* @param string $className Name of the class to load.
*
* @return void
*/
\spl_autoload_register(function(string $className):void {
// Try using class map as defined through /scripts/buildClassMap.php
foreach ([
__DIR__ . '/../tests',
__DIR__ . '/../src',
__DIR__ . '/../src/Checks',
__DIR__ . '/../src/Checks/Puqi',
__DIR__ . '/../src/Checks/Plausi',
__DIR__ . '/../../importer/dependencies/MDAllowedValueSets/src',
__DIR__ . '/../../importer/dependencies/MDAllowedValueSets/exceptions',
__DIR__ . '/../../importer/dependencies/MDErrorReporter/src',
__DIR__ . '/../../importer/dependencies/MDErrorReporter/exceptions',
__DIR__ . '/../../importer/dependencies/MDTlLoader/src',
__DIR__ . '/../../importer/dependencies/MDTlLoader/exceptions',
] as $classDir) {
if (\file_exists("$classDir/$className.php")) {
include "$classDir/$className.php";
return;
}
}
});
restore_error_handler();
restore_exception_handler();