Compare commits
4 Commits
f03bde7c50
...
master
Author | SHA1 | Date | |
---|---|---|---|
a0621216ec
|
|||
8500a401d0
|
|||
9af9eb0560
|
|||
fe59eab399
|
@ -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
|
||||
|
133
src/Checks/Minimaldatensatz/MinimaldatensatzCheck.php
Normal file
133
src/Checks/Minimaldatensatz/MinimaldatensatzCheck.php
Normal 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;
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
3
src/Checks/Minimaldatensatz/README.md
Normal file
3
src/Checks/Minimaldatensatz/README.md
Normal 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
|
@ -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,
|
||||
);
|
||||
|
||||
}
|
||||
|
31
tests/MinimaldatensatzCheckTest.php
Normal file
31
tests/MinimaldatensatzCheckTest.php
Normal 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();
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user