xref: /webtrees/app/Statistics/Repository/MediaRepository.php (revision 71378461661e7642e52abe7d41c9cfffb3e5369b)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2019 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17declare(strict_types=1);
18
19namespace Fisharebest\Webtrees\Statistics\Repository;
20
21use Fisharebest\Webtrees\I18N;
22use Fisharebest\Webtrees\Statistics\Google\ChartMedia;
23use Fisharebest\Webtrees\Statistics\Repository\Interfaces\MediaRepositoryInterface;
24use Fisharebest\Webtrees\Tree;
25use Illuminate\Database\Capsule\Manager as DB;
26use Illuminate\Database\Query\Builder;
27
28use function array_slice;
29use function count;
30use function in_array;
31
32/**
33 * A repository providing methods for media type related statistics.
34 */
35class MediaRepository implements MediaRepositoryInterface
36{
37    /**
38     * @var Tree
39     */
40    private $tree;
41
42    /**
43     * Available media types.
44     */
45    private const MEDIA_TYPE_ALL         = 'all';
46    private const MEDIA_TYPE_AUDIO       = 'audio';
47    private const MEDIA_TYPE_BOOK        = 'book';
48    private const MEDIA_TYPE_CARD        = 'card';
49    private const MEDIA_TYPE_CERTIFICATE = 'certificate';
50    private const MEDIA_TYPE_COAT        = 'coat';
51    private const MEDIA_TYPE_DOCUMENT    = 'document';
52    private const MEDIA_TYPE_ELECTRONIC  = 'electronic';
53    private const MEDIA_TYPE_FICHE       = 'fiche';
54    private const MEDIA_TYPE_FILM        = 'film';
55    private const MEDIA_TYPE_MAGAZINE    = 'magazine';
56    private const MEDIA_TYPE_MANUSCRIPT  = 'manuscript';
57    private const MEDIA_TYPE_MAP         = 'map';
58    private const MEDIA_TYPE_NEWSPAPER   = 'newspaper';
59    private const MEDIA_TYPE_PAINTING    = 'painting';
60    private const MEDIA_TYPE_PHOTO       = 'photo';
61    private const MEDIA_TYPE_TOMBSTONE   = 'tombstone';
62    private const MEDIA_TYPE_VIDEO       = 'video';
63    private const MEDIA_TYPE_OTHER       = 'other';
64    private const MEDIA_TYPE_UNKNOWN     = 'unknown';
65
66    /**
67     * List of GEDCOM media types.
68     *
69     * @var string[]
70     */
71    private const MEDIA_TYPES = [
72        self::MEDIA_TYPE_AUDIO,
73        self::MEDIA_TYPE_BOOK,
74        self::MEDIA_TYPE_CARD,
75        self::MEDIA_TYPE_CERTIFICATE,
76        self::MEDIA_TYPE_COAT,
77        self::MEDIA_TYPE_DOCUMENT,
78        self::MEDIA_TYPE_ELECTRONIC,
79        self::MEDIA_TYPE_FICHE,
80        self::MEDIA_TYPE_FILM,
81        self::MEDIA_TYPE_MAGAZINE,
82        self::MEDIA_TYPE_MANUSCRIPT,
83        self::MEDIA_TYPE_MAP,
84        self::MEDIA_TYPE_NEWSPAPER,
85        self::MEDIA_TYPE_PAINTING,
86        self::MEDIA_TYPE_PHOTO,
87        self::MEDIA_TYPE_TOMBSTONE,
88        self::MEDIA_TYPE_VIDEO,
89        self::MEDIA_TYPE_OTHER,
90    ];
91
92    /**
93     * Constructor.
94     *
95     * @param Tree $tree
96     */
97    public function __construct(Tree $tree)
98    {
99        $this->tree = $tree;
100    }
101
102    /**
103     * Returns the number of media records of the given type.
104     *
105     * @param string $type The media type to query
106     *
107     * @return int
108     */
109    private function totalMediaTypeQuery(string $type): int
110    {
111        if (($type !== self::MEDIA_TYPE_ALL)
112            && ($type !== self::MEDIA_TYPE_UNKNOWN)
113            && !in_array($type, self::MEDIA_TYPES, true)
114        ) {
115            return 0;
116        }
117
118        $query = DB::table('media')
119            ->where('m_file', '=', $this->tree->id());
120
121        if ($type !== self::MEDIA_TYPE_ALL) {
122            if ($type === self::MEDIA_TYPE_UNKNOWN) {
123                // There has to be a better way then this :(
124                foreach (self::MEDIA_TYPES as $t) {
125                    // Use function to add brackets
126                    $query->where(static function (Builder $query) use ($t): void {
127                        $query->where('m_gedcom', 'not like', '%3 TYPE ' . $t . '%')
128                            ->where('m_gedcom', 'not like', '%1 _TYPE ' . $t . '%');
129                    });
130                }
131            } else {
132                // Use function to add brackets
133                $query->where(static function (Builder $query) use ($type): void {
134                    $query->where('m_gedcom', 'like', '%3 TYPE ' . $type . '%')
135                        ->orWhere('m_gedcom', 'like', '%1 _TYPE ' . $type . '%');
136                });
137            }
138        }
139
140        return $query->count();
141    }
142
143    /**
144     * @inheritDoc
145     */
146    public function totalMedia(): string
147    {
148        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_ALL));
149    }
150
151    /**
152     * @inheritDoc
153     */
154    public function totalMediaAudio(): string
155    {
156        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_AUDIO));
157    }
158
159    /**
160     * @inheritDoc
161     */
162    public function totalMediaBook(): string
163    {
164        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_BOOK));
165    }
166
167    /**
168     * @inheritDoc
169     */
170    public function totalMediaCard(): string
171    {
172        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_CARD));
173    }
174
175    /**
176     * @inheritDoc
177     */
178    public function totalMediaCertificate(): string
179    {
180        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_CERTIFICATE));
181    }
182
183    /**
184     * @inheritDoc
185     */
186    public function totalMediaCoatOfArms(): string
187    {
188        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_COAT));
189    }
190
191    /**
192     * @inheritDoc
193     */
194    public function totalMediaDocument(): string
195    {
196        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_DOCUMENT));
197    }
198
199    /**
200     * @inheritDoc
201     */
202    public function totalMediaElectronic(): string
203    {
204        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_ELECTRONIC));
205    }
206
207    /**
208     * @inheritDoc
209     */
210    public function totalMediaFiche(): string
211    {
212        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_FICHE));
213    }
214
215    /**
216     * @inheritDoc
217     */
218    public function totalMediaFilm(): string
219    {
220        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_FILM));
221    }
222
223    /**
224     * @inheritDoc
225     */
226    public function totalMediaMagazine(): string
227    {
228        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MAGAZINE));
229    }
230
231    /**
232     * @inheritDoc
233     */
234    public function totalMediaManuscript(): string
235    {
236        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MANUSCRIPT));
237    }
238
239    /**
240     * @inheritDoc
241     */
242    public function totalMediaMap(): string
243    {
244        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MAP));
245    }
246
247    /**
248     * @inheritDoc
249     */
250    public function totalMediaNewspaper(): string
251    {
252        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_NEWSPAPER));
253    }
254
255    /**
256     * @inheritDoc
257     */
258    public function totalMediaPainting(): string
259    {
260        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_PAINTING));
261    }
262
263    /**
264     * @inheritDoc
265     */
266    public function totalMediaPhoto(): string
267    {
268        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_PHOTO));
269    }
270
271    /**
272     * @inheritDoc
273     */
274    public function totalMediaTombstone(): string
275    {
276        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_TOMBSTONE));
277    }
278
279    /**
280     * @inheritDoc
281     */
282    public function totalMediaVideo(): string
283    {
284        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_VIDEO));
285    }
286
287    /**
288     * @inheritDoc
289     */
290    public function totalMediaOther(): string
291    {
292        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_OTHER));
293    }
294
295    /**
296     * @inheritDoc
297     */
298    public function totalMediaUnknown(): string
299    {
300        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_UNKNOWN));
301    }
302
303    /**
304     * Returns a sorted list of media types and their total counts.
305     *
306     * @param int $tot The total number of media files
307     *
308     * @return array
309     */
310    private function getSortedMediaTypeList(int $tot): array
311    {
312        $media = [];
313        $c     = 0;
314        $max   = 0;
315
316        foreach (self::MEDIA_TYPES as $type) {
317            $count = $this->totalMediaTypeQuery($type);
318
319            if ($count > 0) {
320                $media[$type] = $count;
321
322                if ($count > $max) {
323                    $max = $count;
324                }
325
326                $c += $count;
327            }
328        }
329
330        $count = $this->totalMediaTypeQuery(self::MEDIA_TYPE_UNKNOWN);
331        if ($count > 0) {
332            $media[self::MEDIA_TYPE_UNKNOWN] = $tot - $c;
333            if ($tot - $c > $max) {
334                $max = $count;
335            }
336        }
337
338        if (count($media) > 10 && ($max / $tot) > 0.6) {
339            arsort($media);
340            $media = array_slice($media, 0, 10);
341            $c     = $tot;
342
343            foreach ($media as $cm) {
344                $c -= $cm;
345            }
346
347            if (isset($media[self::MEDIA_TYPE_OTHER])) {
348                $media[self::MEDIA_TYPE_OTHER] += $c;
349            } else {
350                $media[self::MEDIA_TYPE_OTHER] = $c;
351            }
352        }
353
354        asort($media);
355
356        return $media;
357    }
358
359    /**
360     * @inheritDoc
361     */
362    public function chartMedia(string $color_from = null, string $color_to = null): string
363    {
364        $tot   = $this->totalMediaTypeQuery(self::MEDIA_TYPE_ALL);
365        $media = $this->getSortedMediaTypeList($tot);
366
367        return (new ChartMedia())
368            ->chartMedia($media, $color_from, $color_to);
369    }
370}
371