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