diff --git a/README.md b/README.md index 725586c..3212949 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ # Quality Assessment Tools at museum-digital -The classes of this library provide tools to access the quality of museum object records and provide tips for improving it. +The classes of this library provide tools to access the quality of museum object +records and provide tips for improving it. + +## Tools + +- PuQI + +PuQI (class `MDPuqi`) generates a score determining the quality of object metadata +based on e.g. counting linked entries. + +## Plausi + +Plausi analyzes an object's linked events (e.g. production, use) and identifies +logical inconsistencies. + +## Publications + +### PuQI + +Rohde-Enslin, S. (2015). PuQI – A Smart Way to Create Better Data. Uncommon Culture, 6(2), 122-129. diff --git a/src/Checks/Plausi/MDEventCategory.php b/src/Checks/Plausi/MDEventCategory.php new file mode 100644 index 0000000..6a4658f --- /dev/null +++ b/src/Checks/Plausi/MDEventCategory.php @@ -0,0 +1,75 @@ + + */ +declare(strict_types = 1); + +/** + * Represents a higher level category of event types. + */ +enum MDEventCategory implements JsonSerializable { + + case production; + case post_production; + case pre_production; + case no_production; + + /** + * Returns an MDEventCategory based on a given event type. + * + * @param integer $event_type Event type. + * + * @return MDEventCategory + */ + public static function fromEventType(int $event_type):MDEventCategory { + + if (in_array($event_type, MDEventsSet::EVENTS_PRODUCTION, true)) { + return self::production; + } + if (in_array($event_type, MDEventsSet::EVENTS_POST_PRODUCTION, true)) { + return self::post_production; + } + if (in_array($event_type, MDEventsSet::EVENTS_PRE_PRODUCTION, true)) { + return self::pre_production; + } + if (in_array($event_type, MDEventsSet::EVENTS_NO_PRODUCTION, true)) { + return self::no_production; + } + if ($event_type === 5) { + return self::no_production; + } + throw new Exception("Uncategorized event type: " . $event_type); + + } + + /** + * 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/Checks/Plausi/MDPlausi.php b/src/Checks/Plausi/MDPlausi.php new file mode 100644 index 0000000..b26f9fc --- /dev/null +++ b/src/Checks/Plausi/MDPlausi.php @@ -0,0 +1,616 @@ + + */ +declare(strict_types = 1); + +/** + * Plausability checker for object events. + */ +final class MDPlausi { + + const UNKNOWN_TIME_MAX = MDRequirementsSet::TIME_LATEST_YEAR; + const UNKNOWN_TIME_MIN = MDRequirementsSet::TIME_EARLIEST_YEAR; + + private readonly int $default_time_max; + private readonly int $default_time_min; + + private MDTlLoader $_tlLoader; + + /** @var array */ + private array $_events; + /** @var array */ + private array $_messageList = []; + private bool $_hasCertainWarnings = false; // True === 'alarm' + + private bool $_is_print_material; + private bool $_is_painted_drawn; + + private MDPlausiEventCategory $_production; + private MDPlausiEventCategory $_pre_production; + private MDPlausiEventCategory $_post_production; + private MDPlausiEventCategory $_printing_plate; + private MDPlausiEventCategory $_template_creation; + + /** + * Checks whether events themselves are logical regarding the birth and + * death dates vis-a-vis the stated time. + * + * @return void + */ + private function _checkPlausibilityOnBirthDeath():void { + + if (empty($this->_events)) return; + + // Get values for time_entries and actor_life dates from nodac + + foreach ($this->_events as $event) { + + // Evaluating actor birth + if ($event->actor_birth_normalized !== false) { + + // Work on the object started before the actor was born + if ($event->time_start_normalized !== false && $event->time_start_normalized < $event->actor_birth_normalized) { + $this->_messageList[] = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$event->event_type) . ' (' . $event->time_name . ') ' . $this->_tlLoader->tl("quality", "quality", "before_birth_possibly") . ' ' . $event->actor_name; + } + + // The actor was involved with the object totally before they were born + // Ended being involved before birth + + else if ($event->time_end_normalized !== false && $event->time_end_normalized < $event->actor_birth_normalized) { + $this->_messageList[] = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$event->event_type) . ' (' . $event->time_name . ') ' . $this->_tlLoader->tl("quality", "quality", "before_birth") . ' ' . $event->actor_name; + $this->_hasCertainWarnings = true; + } + + } + + // Evaluate actor death + if ($event->actor_death_normalized !== false) { + + // Check if the actor died before they started being involved with the object + if ($event->time_start_normalized !== false && $event->actor_death_normalized < $event->time_start_normalized) { + $this->_messageList[] = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$event->event_type) . ' (' . $event->time_name . ') ' . $this->_tlLoader->tl("quality", "quality", "after_death") . ' ' . $event->actor_name; + $this->_hasCertainWarnings = true; + } + else if ($event->time_end_normalized !== false && $event->actor_death_normalized < $event->time_end_normalized) { + $this->_messageList[] = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$event->event_type) . ' (' . $event->time_name . ') ' . $this->_tlLoader->tl("quality", "quality", "after_death_possibly") . ' ' . $event->actor_name; + } + + } + + } + + } + + /** + * Evaluates the relation between template creation, printing plate or drawing dates. + * + * @return void + */ + public function _evaluateTemplateStatus():void { + + // Printing plate was produced before template was even started + if ($this->_printing_plate->latest_event_type !== 0 + && $this->_template_creation->earliest_event_type !== 0 + && $this->_printing_plate->latest < $this->_template_creation->earliest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", "12") . ''; + + $message .= $this->_printing_plate->formulateMessagePartForLatest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", "4") . ''; + + $message .= $this->_template_creation->formulateMessagePartForEarliest($this->_tlLoader); + + $this->_messageList[] = $message; + $this->_hasCertainWarnings = true; + + } + // Printing plate was produced before template was finished + else if ($this->_printing_plate->latest_event_type !== 0 + && $this->_template_creation->latest_event_type !== 0 + && $this->_printing_plate->latest < $this->_template_creation->latest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", "12") . ''; + + $message .= $this->_printing_plate->formulateMessagePartForLatest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before_possibly") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", "4") . ''; + + $message .= $this->_template_creation->formulateMessagePartForLatest($this->_tlLoader); + + $this->_messageList[] = $message; + + } + + + } + + /** + * Adds a warning message if an object was both drawn and printed + * (prints of drawn materials make the drawing a template). + * + * @return void + */ + private function _evaluateDrawingsWhichArePrinted():void { + + // Paintings should not be printed at the same time + if ($this->_is_painted_drawn === true and $this->_is_print_material === true) { + + $message = '' . $this->_tlLoader->tl("quality", "quality", "print_not_paint") . ' ' . $this->_tlLoader->tl("quality", "quality", "better_use") . ' "' . $this->_tlLoader->tl("eventtype_name", "eventname", "4") . '" . '; + $this->_hasCertainWarnings = true; + + } + + } + + /** + * Evaluates if an object's preproduction (e.g. template) took place after the object was + * already produced. + * + * @return void + */ + private function _evaluatePreproductionAfterProduction():void { + + // Printing plate was produced before template was finished + if ($this->_production->latest_event_type !== 0 + && $this->_pre_production->earliest_event_type !== 0 + && $this->_production->latest < $this->_pre_production->earliest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->latest_event_type) . ''; + + $message .= $this->_production->formulateMessagePartForLatest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_pre_production->earliest_event_type) . ''; + + $message .= $this->_pre_production->formulateMessagePartForEarliest($this->_tlLoader); + + $this->_messageList[] = $message; + $this->_hasCertainWarnings = true; + + } + + // Printing plate was produced before template was finished + else if ($this->_production->latest_event_type !== 0 + && $this->_pre_production->latest_event_type !== 0 + && $this->_production->latest < $this->_pre_production->latest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->latest_event_type) . ''; + + $message .= $this->_production->formulateMessagePartForLatest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before_possibly") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_pre_production->latest_event_type) . ''; + + $message .= $this->_pre_production->formulateMessagePartForLatest($this->_tlLoader); + + $this->_messageList[] = $message; + + } + + } + + /** + * Evaluates if an object's preproduction (e.g. template) took place after the object was + * already being used (or otherwise interacted with past it's completion). + * + * @return void + */ + private function _evaluatePreproductionAfterPostproduction():void { + + // Printing plate was postproduced before template was finished + if ($this->_post_production->latest_event_type !== 0 + && $this->_pre_production->earliest_event_type !== 0 + && $this->_post_production->latest < $this->_pre_production->earliest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_post_production->latest_event_type) . ''; + + $message .= $this->_post_production->formulateMessagePartForLatest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_pre_production->earliest_event_type) . ''; + + $message .= $this->_pre_production->formulateMessagePartForEarliest($this->_tlLoader); + + $this->_messageList[] = $message; + $this->_hasCertainWarnings = true; + + } + + // E.g. template creation after being all use was finished + else if ($this->_post_production->latest_event_type !== 0 + && $this->_pre_production->latest_event_type !== 0 + && $this->_post_production->latest < $this->_pre_production->latest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_post_production->latest_event_type) . ''; + + $message .= $this->_post_production->formulateMessagePartForLatest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_pre_production->latest_event_type) . ''; + + $message .= $this->_pre_production->formulateMessagePartForLatest($this->_tlLoader); + + $this->_messageList[] = $message; + $this->_hasCertainWarnings = true; + + } + + // E.g. template creation was only started after the object was beyond use + // This may happen, if the template is for renovations or so. Hence, there is + // only a warning here. + else if ($this->_post_production->earliest_event_type !== 0 + && $this->_pre_production->earliest_event_type !== 0 + && $this->_post_production->earliest < $this->_pre_production->earliest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_post_production->earliest_event_type) . ''; + + $message .= $this->_post_production->formulateMessagePartForEarliest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before_possibly") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_pre_production->earliest_event_type) . ''; + + $message .= $this->_pre_production->formulateMessagePartForEarliest($this->_tlLoader); + + $this->_messageList[] = $message; + + } + + } + + /** + * Evaluates if an object's production (e.g. template) took place after the object was + * already being used (or otherwise interacted with past it's completion). + * + * @return void + */ + private function _evaluateProductionAfterPostproduction():void { + + 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 .= ' ' . $this->_tlLoader->tl("quality", "quality", "before") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->earliest_event_type) . ''; + + $message .= $this->_production->formulateMessagePartForEarliest($this->_tlLoader); + + $this->_messageList[] = $message; + $this->_hasCertainWarnings = true; + + } + + else if ($this->_post_production->earliest_event_type !== 0 + && $this->_production->latest_event_type !== 0 + && $this->_post_production->earliest < $this->_production->latest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_post_production->earliest_event_type) . ''; + + $message .= $this->_post_production->formulateMessagePartForEarliest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before_possibly") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->latest_event_type) . ''; + + $message .= $this->_production->formulateMessagePartForLatest($this->_tlLoader); + + $this->_messageList[] = $message; + + } + else if ($this->_post_production->latest_event_type !== 0 + && $this->_production->latest_event_type !== 0 + && $this->_post_production->latest < $this->_production->latest) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_post_production->latest_event_type) . ''; + + $message .= $this->_post_production->formulateMessagePartForLatest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before_possibly") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->latest_event_type) . ''; + + $message .= $this->_production->formulateMessagePartForLatest($this->_tlLoader); + + $this->_messageList[] = $message; + + } + + } + + /** + * Warns if an actor was displayed on an object finished before they were born. + * + * @return void + */ + private function _checkActorDisplayedBeforeBirth():void { + + foreach ($this->_events as $event) { + + // Ignore events other than "was displayed" + if ($event->event_type !== 5) continue; + + if ($event->actor_birth_normalized !== false + && $this->_production->latest_event_type !== 0 + && $event->actor_birth_normalized > $this->_production->latest + ) { + + $message = '' . $this->_tlLoader->tl("eventtype_name", "eventname", (string)$this->_production->latest_event_type) . ''; + + $message .= $this->_production->formulateMessagePartForLatest($this->_tlLoader); + + $message .= ' ' . $this->_tlLoader->tl("quality", "quality", "before") . ' ' . $this->_tlLoader->tl("eventtype_name", "eventname", "5") . ''; + $message .= ' (' . $this->_tlLoader->tl("basis", "basis", "actor_short") . ': ' . $event->actor_name . ' (* ' . $event->actor_birth . '))'; + + $this->_messageList[] = $message; + $this->_hasCertainWarnings = true; + + } + + } + + } + + /** + * Checks plausibility by event category. + * + * @return void + */ + private function _checkIllogicalEventsByCategory():void { + + $this->_evaluateTemplateStatus(); + $this->_evaluateDrawingsWhichArePrinted(); + $this->_evaluatePreproductionAfterProduction(); + $this->_evaluatePreproductionAfterPostproduction(); + $this->_evaluateProductionAfterPostproduction(); + + } + + + /** + * Runs the available evaluations. + * + * @return void + */ + public function evaluate():void { + + $this->_checkPlausibilityOnBirthDeath(); + $this->_checkIllogicalEventsByCategory(); + $this->_checkActorDisplayedBeforeBirth(); + + } + + /** + * Setup function for setting event categories. + * + * @return void + */ + public function _categorizeEvents():void { + + foreach ($this->_events as $event) { + + if ($event->actor_death_normalized !== false) { + $actor_death = date("Y", $event->actor_death_normalized); + } + else $actor_death = self::UNKNOWN_TIME_MIN; + + switch ($event->event_category) { + case MDEventCategory::production: + + if ($event->time_start_normalized !== false && $this->_production->earliest > $event->time_start_normalized) { + $this->_production->earliest = $event->time_start_normalized; + $this->_production->earliest_name = $event->time_name; + $this->_production->earliest_time_name = $event->time_start; + $this->_production->earliest_source = 'time'; + $this->_production->earliest_event_type = $event->event_type; + } + if ($event->time_end_normalized !== false && $this->_production->latest < $event->time_end_normalized) { + $this->_production->latest = $event->time_end_normalized; + $this->_production->latest_name = $event->time_name; + $this->_production->latest_time_name = $event->time_end; + $this->_production->latest_source = 'time'; + $this->_production->latest_event_type = $event->event_type; + } + if ($event->actor_birth_normalized !== false && $this->_production->earliest > $event->actor_birth_normalized) { + $this->_production->earliest = $event->actor_birth_normalized; + $this->_production->earliest_name = $event->actor_name; + $this->_production->earliest_time_name = $event->actor_birth; + $this->_production->earliest_source = 'actor'; + $this->_production->earliest_event_type = $event->event_type; + } + if ($event->actor_death_normalized !== false && $this->_production->latest < $event->actor_death_normalized) { + $this->_production->latest = $event->actor_death_normalized; + $this->_production->latest_name = $event->actor_name; + $this->_production->latest_time_name = $event->actor_death; + $this->_production->latest_source = 'actor'; + $this->_production->latest_event_type = $event->event_type; + } + + break; + + case MDEventCategory::pre_production: + + if ($event->time_start_normalized !== false && $this->_pre_production->earliest > $event->time_start_normalized) { + $this->_pre_production->earliest = $event->time_start_normalized; + $this->_pre_production->earliest_name = $event->time_name; + $this->_pre_production->earliest_time_name = $event->time_start; + $this->_pre_production->earliest_source = 'time'; + $this->_pre_production->earliest_event_type = $event->event_type; + } + if ($event->time_end_normalized !== false && $this->_pre_production->latest < $event->time_end_normalized) { + $this->_pre_production->latest = $event->time_end_normalized; + $this->_pre_production->latest_name = $event->time_name; + $this->_pre_production->latest_time_name = $event->time_end; + $this->_pre_production->latest_source = 'time'; + $this->_pre_production->latest_event_type = $event->event_type; + } + if ($event->actor_birth_normalized !== false && $this->_pre_production->earliest > $event->actor_birth_normalized) { + $this->_pre_production->earliest = $event->actor_birth_normalized; + $this->_pre_production->earliest_name = $event->actor_name; + $this->_pre_production->earliest_time_name = $event->actor_birth; + $this->_pre_production->earliest_source = 'actor'; + $this->_pre_production->earliest_event_type = $event->event_type; + } + if ($event->actor_death_normalized !== false && $this->_pre_production->latest < $event->actor_death_normalized) { + $this->_pre_production->latest = $event->actor_death_normalized; + $this->_pre_production->latest_name = $event->actor_name; + $this->_pre_production->latest_time_name = $event->actor_death; + $this->_pre_production->latest_source = 'actor'; + $this->_pre_production->latest_event_type = $event->event_type; + } + + break; + case MDEventCategory::post_production: + + if ($event->time_start_normalized !== false && $this->_post_production->earliest > $event->time_start_normalized) { + $this->_post_production->earliest = $event->time_start_normalized; + $this->_post_production->earliest_name = $event->time_name; + $this->_post_production->earliest_time_name = $event->time_start; + $this->_post_production->earliest_source = 'time'; + $this->_post_production->earliest_event_type = $event->event_type; + } + if ($event->time_end_normalized !== false && $this->_post_production->latest < $event->time_end_normalized) { + $this->_post_production->latest = $event->time_end_normalized; + $this->_post_production->latest_name = $event->time_name; + $this->_post_production->latest_time_name = $event->time_end; + $this->_post_production->latest_source = 'time'; + $this->_post_production->latest_event_type = $event->event_type; + } + if ($event->actor_birth_normalized !== false && $this->_post_production->earliest > $event->actor_birth_normalized) { + $this->_post_production->earliest = $event->actor_birth_normalized; + $this->_post_production->earliest_name = $event->actor_name; + $this->_post_production->earliest_time_name = $event->actor_birth; + $this->_post_production->earliest_source = 'actor'; + $this->_post_production->earliest_event_type = $event->event_type; + } + if ($event->actor_death_normalized !== false && $this->_post_production->latest < $event->actor_death_normalized) { + $this->_post_production->latest = $event->actor_death_normalized; + $this->_post_production->latest_name = $event->actor_name; + $this->_post_production->latest_time_name = $event->actor_death; + $this->_post_production->latest_source = 'actor'; + $this->_post_production->latest_event_type = $event->event_type; + } + + break; + } + + if ($event->event_type === 9 || $event->event_type === 19) { + $this->_is_painted_drawn = true; + } + if ($event->event_type === 4) { + + if ($event->time_start_normalized !== false && $this->_template_creation->earliest > $event->time_start_normalized) { + $this->_template_creation->earliest = $event->time_start_normalized; + $this->_template_creation->earliest_name = $event->time_name; + $this->_template_creation->earliest_time_name = $event->time_start; + $this->_template_creation->earliest_source = 'time'; + $this->_template_creation->earliest_event_type = $event->event_type; + } + if ($event->time_end_normalized !== false && $this->_template_creation->latest < $event->time_end_normalized) { + $this->_template_creation->latest = $event->time_end_normalized; + $this->_template_creation->latest_name = $event->time_name; + $this->_template_creation->latest_time_name = $event->time_end; + $this->_template_creation->latest_source = 'time'; + $this->_template_creation->latest_event_type = $event->event_type; + } + if ($event->actor_birth_normalized !== false && $this->_template_creation->earliest > $event->actor_birth_normalized) { + $this->_template_creation->earliest = $event->actor_birth_normalized; + $this->_template_creation->earliest_name = $event->actor_name; + $this->_template_creation->earliest_time_name = $event->actor_birth; + $this->_template_creation->earliest_source = 'actor'; + $this->_template_creation->earliest_event_type = $event->event_type; + } + if ($event->actor_death_normalized !== false && $this->_template_creation->latest < $event->actor_death_normalized) { + $this->_template_creation->latest = $event->actor_death_normalized; + $this->_template_creation->latest_name = $event->actor_name; + $this->_template_creation->latest_time_name = $event->actor_death; + $this->_template_creation->latest_source = 'actor'; + $this->_template_creation->latest_event_type = $event->event_type; + } + + } + else if ($event->event_type === 12) { + + if ($event->time_start_normalized !== false && $this->_printing_plate->earliest > $event->time_start_normalized) { + $this->_printing_plate->earliest = $event->time_start_normalized; + $this->_printing_plate->earliest_name = $event->time_name; + $this->_printing_plate->earliest_time_name = $event->time_start; + $this->_printing_plate->earliest_source = 'time'; + $this->_printing_plate->earliest_event_type = $event->event_type; + } + if ($event->time_end_normalized !== false && $this->_printing_plate->latest < $event->time_end_normalized) { + $this->_printing_plate->latest = $event->time_end_normalized; + $this->_printing_plate->latest_name = $event->time_name; + $this->_printing_plate->latest_time_name = $event->time_end; + $this->_printing_plate->latest_source = 'time'; + $this->_printing_plate->latest_event_type = $event->event_type; + } + if ($event->actor_birth_normalized !== false && $this->_printing_plate->earliest > $event->actor_birth_normalized) { + $this->_printing_plate->earliest = $event->actor_birth_normalized; + $this->_printing_plate->earliest_name = $event->actor_name; + $this->_printing_plate->earliest_time_name = $event->actor_birth; + $this->_printing_plate->earliest_source = 'actor'; + $this->_printing_plate->earliest_event_type = $event->event_type; + } + if ($event->actor_death_normalized !== false && $this->_printing_plate->latest < $event->actor_death_normalized) { + $this->_printing_plate->latest = $event->actor_death_normalized; + $this->_printing_plate->latest_name = $event->actor_name; + $this->_printing_plate->latest_time_name = $event->actor_death; + $this->_printing_plate->latest_source = 'actor'; + $this->_printing_plate->latest_event_type = $event->event_type; + } + $this->_is_print_material = true; + + } + + } + + if (!isset($this->_is_painted_drawn)) $this->_is_painted_drawn = true; + if (!isset($this->_is_print_material)) $this->_is_print_material = true; + + } + + /** + * Returns the message list. + * + * @return array + */ + public function getMessages():array { + + return $this->_messageList; + + } + + /** + * Returns the warning status. + * + * @return boolean + */ + public function getWarningStatus():bool { + + return $this->_hasCertainWarnings; + + } + + /** + * Constructor. + * + * @param MDTlLoader $tlLoader Translation loader. + * @param array $events Events. + * + * @return void + */ + public function __construct(MDTlLoader $tlLoader, array $events) { + + $this->_tlLoader = $tlLoader; + $this->_events = $events; + + $this->default_time_max = time(); + $this->default_time_min = -99999999999999999; + + $this->_production = new MDPlausiEventCategory($this->default_time_max, $this->default_time_min); + $this->_pre_production = new MDPlausiEventCategory($this->default_time_max, $this->default_time_min); + $this->_post_production = new MDPlausiEventCategory($this->default_time_max, $this->default_time_min); + $this->_printing_plate = new MDPlausiEventCategory($this->default_time_max, $this->default_time_min); + $this->_template_creation = new MDPlausiEventCategory($this->default_time_max, $this->default_time_min); + + $this->_categorizeEvents(); + + } +} diff --git a/src/Checks/Plausi/MDPlausiEvent.php b/src/Checks/Plausi/MDPlausiEvent.php new file mode 100644 index 0000000..21a10c5 --- /dev/null +++ b/src/Checks/Plausi/MDPlausiEvent.php @@ -0,0 +1,99 @@ + + */ +declare(strict_types = 1); + +/** + * An event as required for the plausibility checker for events. + */ +final class MDPlausiEvent { + + public readonly int $event_type; + public readonly MDEventCategory $event_category; + + public readonly string $time_name; + public readonly string $time_start; + public readonly string $time_end; + public readonly int|false $time_start_normalized; + public readonly int|false $time_end_normalized; + + public readonly string $actor_name; + public readonly string $actor_birth; + public readonly string $actor_death; + public readonly int|false $actor_birth_normalized; + public readonly int|false $actor_death_normalized; + + /** + * Attempts to parse a time. + * + * @param string $time Time to parse. + * + * @return array{earliest: int|false, latest: int|false} + */ + private function _attemptParsingTime(string $time):array { + + if (empty($time)) return ['earliest' => false, 'latest' => false]; + + // 2005 + if (in_array(strlen($time), [4, 5], true) and is_numeric(substr($time, 1))) { + return [ + 'earliest' => strtotime($time . '-01-01'), + 'latest' => strtotime($time . '-12-31'), + ]; + } + + // 2005-2006 + if (strlen($time) === 9) { + $parts = explode('-', $time); + if (count($parts) === 2) { + return [ + 'earliest' => strtotime($parts[0]. '-01-01'), + 'latest' => strtotime($parts[1]. '-12-31'), + ]; + } + } + + $strtotime = strtotime($time); + return [ + 'earliest' => $strtotime, + 'latest' => $strtotime, + ]; + + } + + /** + * Constructor. + * + * @param integer $event_type Event type. + * @param string $time_name Time. + * @param string $time_start Start. + * @param string $time_end End. + * @param string $actor_name Name of the linked actor. + * @param string $actor_birth Birth of the linked actor. + * @param string $actor_death Time of death of the linked actor. + * + * @return void + */ + public function __construct(int $event_type, string $time_name, string $time_start, string $time_end, string $actor_name, string $actor_birth, string $actor_death) { + + $this->event_type = $event_type; + $this->time_name = $time_name; + $this->time_start = $time_start; + $this->time_end = $time_end; + $this->actor_name = $actor_name; + $this->actor_birth = $actor_birth; + $this->actor_death = $actor_death; + + $this->time_start_normalized = $this->_attemptParsingTime($time_start)['earliest']; + $this->time_end_normalized = $this->_attemptParsingTime($time_end)['latest']; + + $this->actor_birth_normalized = $this->_attemptParsingTime($actor_birth)['earliest']; + $this->actor_death_normalized = $this->_attemptParsingTime($actor_death)['latest']; + + $this->event_category = MDEventCategory::fromEventType($event_type); + + } +} diff --git a/src/Checks/Plausi/MDPlausiEventCategory.php b/src/Checks/Plausi/MDPlausiEventCategory.php new file mode 100644 index 0000000..33a5ed3 --- /dev/null +++ b/src/Checks/Plausi/MDPlausiEventCategory.php @@ -0,0 +1,78 @@ + + */ +declare(strict_types = 1); + +/** + * Handles event category as used by MDPlausi. + */ +final class MDPlausiEventCategory { + + public int $earliest; + public int $latest; + public string $earliest_name = ''; + public string $latest_name = ''; + public string $earliest_time_name = ''; + public string $latest_time_name = ''; + + /** @var 'actor'|'time' */ + public string $earliest_source; + /** @var 'actor'|'time' */ + public string $latest_source; + public int $earliest_event_type = 0; + public int $latest_event_type = 0; + + /** + * Formulates a plausi message part base on the earliest use of the category. + * + * @param MDTlLoader $tlLoader Translation loader. + * + * @return string + */ + public function formulateMessagePartForEarliest(MDTlLoader $tlLoader):string { + + if ($this->earliest_source === 'actor') { + return ' (' . $tlLoader->tl("basis", "basis", "actor_short") . ': ' . $this->earliest_name . ' (* ' . $this->earliest_time_name . '))'; + } + else { + return ' (' . $this->earliest_name . ')'; + } + + } + + /** + * Formulates a plausi message part base on the latest use of the category. + * + * @param MDTlLoader $tlLoader Translation loader. + * + * @return string + */ + public function formulateMessagePartForLatest(MDTlLoader $tlLoader):string { + + if ($this->latest_source === 'actor') { + return ' (' . $tlLoader->tl("basis", "basis", "actor_short") . ': ' . $this->latest_name . ' (✝ ' . $this->latest_time_name . '))'; + } + else { + return ' (' . $this->latest_name . ')'; + } + + } + + /** + * Constructor. + * + * @param int $default_earliest Default earliest time. + * @param int $default_latest Default latest time. + * + * @return void + */ + public function __construct(int $default_earliest, int $default_latest) { + + $this->earliest = $default_earliest; + $this->latest = $default_latest; + + } +} diff --git a/src/Checks/Plausi/MDPlausiStatus.php b/src/Checks/Plausi/MDPlausiStatus.php new file mode 100644 index 0000000..bad7efc --- /dev/null +++ b/src/Checks/Plausi/MDPlausiStatus.php @@ -0,0 +1,45 @@ + + */ +declare(strict_types = 1); + +/** + * Represents the plausibility check status. + */ +enum MDPlausiStatus implements JsonSerializable { + + case alarm; + case okay; + + /** + * 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; + + } +}