Passed
Push — main ( c808ff...64cea5 )
by Jonathan
03:30
created

SourceStatusService   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 47
dl 0
loc 144
rs 10
c 0
b 0
f 0
wmc 20

7 Methods

Rating   Name   Duplication   Size   Complexity  
A sourceStatusForRecord() 0 15 3
A sourceStatusForFactsWithTags() 0 12 3
B sourceStatusForFact() 0 29 7
A extractCitations() 0 12 4
A sourceStatusForBirth() 0 3 1
A sourceStatusForMarriage() 0 4 1
A sourceStatusForDeath() 0 3 1
1
<?php
2
3
/**
4
 * webtrees-lib: MyArtJaub library for webtrees
5
 *
6
 * @package MyArtJaub\Webtrees
7
 * @subpackage IsSourced
8
 * @author Jonathan Jaubart <[email protected]>
9
 * @copyright Copyright (c) 2009-2022, Jonathan Jaubart
10
 * @license https://www.gnu.org/licenses/gpl.html GNU General Public License, version 3
11
 */
12
13
declare(strict_types=1);
14
15
namespace MyArtJaub\Webtrees\Module\IsSourced\Services;
16
17
use Fisharebest\Webtrees\Date;
18
use Fisharebest\Webtrees\Fact;
19
use Fisharebest\Webtrees\Family;
20
use Fisharebest\Webtrees\Gedcom;
21
use Fisharebest\Webtrees\GedcomRecord;
22
use Fisharebest\Webtrees\Individual;
23
use Fisharebest\Webtrees\Registry;
24
use MyArtJaub\Webtrees\Module\IsSourced\Data\FactSourceStatus;
25
use MyArtJaub\Webtrees\Module\IsSourced\Data\NullFactSourceStatus;
26
use MyArtJaub\Webtrees\Module\IsSourced\Data\SourceStatus;
27
28
/**
29
 * Service for computing the status of sources for records and facts.
30
 */
31
class SourceStatusService
32
{
33
    /**
34
     * Maximum timespan between the date of a source and the date of the event to consider the source precise.
35
     * Arbitrally set to approximately a year around the event date.
36
     *
37
     * @var int DATE_PRECISION_MARGIN
38
     */
39
    private const DATE_PRECISION_MARGIN = 180;
40
41
    /**
42
     * Extract gedcom for all source citations of a fact.
43
     * Logic removed from \Fisharebest\Webtrees\Fact.
44
     *
45
     * @param Fact $fact
46
     * @return string[]
47
     */
48
    private function extractCitations(Fact $fact)
49
    {
50
        $extract_regex = '/\n(2 SOUR @(' . Gedcom::REGEX_XREF . ')@(?:\n[3-9] .*)*)/';
51
        preg_match_all($extract_regex, $fact->gedcom(), $matches, PREG_SET_ORDER);
52
        $citations = [];
53
        foreach ($matches as $match) {
54
            $source = Registry::sourceFactory()->make($match[2], $fact->record()->tree());
55
            if ($source !== null && $source->canShow()) {
56
                $citations[] = $match[1];
57
            }
58
        }
59
        return $citations;
60
    }
61
62
    /**
63
     * Return the status of source citations for a fact.
64
     *
65
     * @param Fact $fact
66
     * @return FactSourceStatus
67
     */
68
    public function sourceStatusForFact(Fact $fact): FactSourceStatus
69
    {
70
        $source_status = new FactSourceStatus();
71
72
        $date = $fact->date();
73
        $source_status
74
            ->setFactHasDate($date->isOK())
75
            ->setFactHasPreciseDate($date->qual1 === '' && $date->minimumJulianDay() === $date->maximumJulianDay());
76
77
        foreach ($this->extractCitations($fact) as $citation) {
78
            $source_status
79
                ->setHasSource(true)
80
                ->addHasSupportingDocument(preg_match('/\n3 _ACT (?:.*)/', $citation) === 1);
81
82
            preg_match_all("/\n3 DATA(?:\n[4-9] .*)*\n4 DATE (.*)/", $citation, $date_matches, PREG_SET_ORDER);
83
            foreach ($date_matches as $date_match) {
84
                $source_date = new Date($date_match[1]);
85
                $source_status
86
                    ->addSourceHasDate($source_date->isOK())
87
                    ->addSourceMatchesFactDate($date->isOK() && $source_date->isOK()
88
                        && abs($source_date->julianDay() - $date->julianDay()) < self::DATE_PRECISION_MARGIN);
89
            }
90
91
            if ($source_status->isFullySourced()) {
92
                return $source_status;
93
            }
94
        }
95
96
        return $source_status;
97
    }
98
99
    /**
100
     * Return the status of sources for a Gedcom record.
101
     *
102
     * @param GedcomRecord $record
103
     * @return SourceStatus
104
     */
105
    public function sourceStatusForRecord(GedcomRecord $record): SourceStatus
106
    {
107
        $source_status = new SourceStatus();
108
109
        foreach ($record->facts(['SOUR']) as $source) {
110
            $source_status
111
                ->setHasSource(true)
112
                ->addHasSupportingDocument($source->attribute('_ACT') !== '');
113
114
            if ($source_status->isFullySourced()) {
115
                return $source_status;
116
            }
117
        }
118
119
        return $source_status;
120
    }
121
122
    /**
123
     * Return the status of source citations for a list of fact types associated with a record.
124
     *
125
     * @param GedcomRecord $record
126
     * @param string[] $tags
127
     * @return FactSourceStatus
128
     */
129
    public function sourceStatusForFactsWithTags(GedcomRecord $record, array $tags): FactSourceStatus
130
    {
131
        $source_status = new NullFactSourceStatus();
132
133
        foreach ($record->facts($tags) as $fact) {
134
            $source_status = $source_status->combineWith($this->sourceStatusForFact($fact));
135
            if ($source_status->isFullySourced()) {
136
                return $source_status;
137
            }
138
        }
139
140
        return $source_status;
141
    }
142
143
    /**
144
     * Return the status of source citations for an individual's birth events.
145
     *
146
     * @param Individual $individual
147
     * @return FactSourceStatus
148
     */
149
    public function sourceStatusForBirth(Individual $individual): FactSourceStatus
150
    {
151
        return $this->sourceStatusForFactsWithTags($individual, Gedcom::BIRTH_EVENTS);
152
    }
153
154
    /**
155
     * Return the status of source citations for an individual's death events.
156
     *
157
     * @param Individual $individual
158
     * @return FactSourceStatus
159
     */
160
    public function sourceStatusForDeath(Individual $individual): FactSourceStatus
161
    {
162
        return $this->sourceStatusForFactsWithTags($individual, Gedcom::DEATH_EVENTS);
163
    }
164
165
    /**
166
     * Return the status of source citations for a family's marriage events.
167
     *
168
     * @param Family $family
169
     * @return FactSourceStatus
170
     */
171
    public function sourceStatusForMarriage(Family $family): FactSourceStatus
172
    {
173
        $marr_events = [...Gedcom::MARRIAGE_EVENTS, 'MARC', 'MARL', 'MARS'];
174
        return $this->sourceStatusForFactsWithTags($family, $marr_events);
175
    }
176
}
177