Compare commits

...

4 Commits

6 changed files with 259 additions and 61 deletions

View File

@ -5,7 +5,7 @@ records and provide tips for improving it.
## Tools
- PuQI
### PuQI
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,
@ -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
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
### PuQI

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -30,71 +30,71 @@ final class MDPuqi {
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
public const THRESHOLD_TAGS_TOO_MANY = 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
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"];
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"];
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;
public const QI_REWARD_DESCRIPTION_LENGTH_GOOD = 3;
private const QI_REWARD_DESCRIPTION_LENGTH_GREAT = 6;
private const QI_REWARD_EVENTS_MULTIPLIER = 5;
private const QI_REWARD_TAGS_MULTIPLIER = 3;
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;
private const QI_REWARD_IMGS_RESOURCES_MULTIPLIER = 3;
private 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;
const QI_REWARD_PUBLIC_INSCRIPTION = 3;
const QI_REWARD_PUBLIC_COMPARABLE_OBJECTS = 3;
const QI_REWARD_PUBLIC_DETAILED_DESCRIPTION = 5;
private const QI_REWARD_PUBLIC_INSCRIPTION = 3;
private const QI_REWARD_PUBLIC_COMPARABLE_OBJECTS = 3;
private 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;
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;
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
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;
private const QI_PENALTY_NO_EVENTS = -15;
private const QI_PENALTY_NO_TAG = -15;
private const QI_PENALTY_ONLY_ONE_TAG = -10;
private const QI_PENALTY_ONLY_TWO_TAG = -5;
private 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;
private const QI_PENALTY_IMAGE_FILE_MISSING = -5;
private const QI_PENALTY_IMAGE_TOO_SMALL = -5;
private 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;
@ -740,7 +740,7 @@ final class MDPuqi {
MDPuqiCheckSection::markingCount,
MDPuqiMessageStatus::praise,
$this->_tlLoader->tl("quality", "quality", "markings_exist"),
self::QI_REWARD_OBJ_IS_REF_MULTIPLIER * $count,
self::QI_REWARD_MARKINGS_MULTIPLIER * $count,
);
}

View File

@ -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();
}
}