xref: /webtrees/app/Statistics/Repository/MediaRepository.php (revision 4c78e066e3ba48f4c71bb900fac88b9e85e97474)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Statistics\Repository;
21
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;
28
29use function array_slice;
30use function arsort;
31use function asort;
32use function count;
33use function in_array;
34
35/**
36 * A repository providing methods for media type related statistics.
37 */
38class MediaRepository implements MediaRepositoryInterface
39{
40    private Tree $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    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     * @param Tree $tree
92     */
93    public function __construct(Tree $tree)
94    {
95        $this->tree = $tree;
96    }
97
98    /**
99     * Returns the number of media records of the given type.
100     *
101     * @param string $type The media type to query
102     *
103     * @return int
104     */
105    private function totalMediaTypeQuery(string $type): int
106    {
107        if ($type !== self::MEDIA_TYPE_ALL && $type !== self::MEDIA_TYPE_UNKNOWN && !in_array($type, self::MEDIA_TYPES, true)) {
108            return 0;
109        }
110
111        $query = DB::table('media')
112            ->where('m_file', '=', $this->tree->id());
113
114        if ($type !== self::MEDIA_TYPE_ALL) {
115            if ($type === self::MEDIA_TYPE_UNKNOWN) {
116                // There has to be a better way then this :(
117                foreach (self::MEDIA_TYPES as $t) {
118                    // Use function to add brackets
119                    $query->where(static function (Builder $query) use ($t): void {
120                        $query->where('m_gedcom', 'not like', '%3 TYPE ' . $t . '%')
121                            ->where('m_gedcom', 'not like', '%1 _TYPE ' . $t . '%');
122                    });
123                }
124            } else {
125                // Use function to add brackets
126                $query->where(static function (Builder $query) use ($type): void {
127                    $query->where('m_gedcom', 'like', '%3 TYPE ' . $type . '%')
128                        ->orWhere('m_gedcom', 'like', '%1 _TYPE ' . $type . '%');
129                });
130            }
131        }
132
133        return $query->count();
134    }
135
136    /**
137     * @return string
138     */
139    public function totalMedia(): string
140    {
141        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_ALL));
142    }
143
144    /**
145     * @return string
146     */
147    public function totalMediaAudio(): string
148    {
149        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_AUDIO));
150    }
151
152    /**
153     * @return string
154     */
155    public function totalMediaBook(): string
156    {
157        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_BOOK));
158    }
159
160    /**
161     * @return string
162     */
163    public function totalMediaCard(): string
164    {
165        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_CARD));
166    }
167
168    /**
169     * @return string
170     */
171    public function totalMediaCertificate(): string
172    {
173        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_CERTIFICATE));
174    }
175
176    /**
177     * @return string
178     */
179    public function totalMediaCoatOfArms(): string
180    {
181        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_COAT));
182    }
183
184    /**
185     * @return string
186     */
187    public function totalMediaDocument(): string
188    {
189        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_DOCUMENT));
190    }
191
192    /**
193     * @return string
194     */
195    public function totalMediaElectronic(): string
196    {
197        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_ELECTRONIC));
198    }
199
200    /**
201     * @return string
202     */
203    public function totalMediaFiche(): string
204    {
205        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_FICHE));
206    }
207
208    /**
209     * @return string
210     */
211    public function totalMediaFilm(): string
212    {
213        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_FILM));
214    }
215
216    /**
217     * @return string
218     */
219    public function totalMediaMagazine(): string
220    {
221        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MAGAZINE));
222    }
223
224    /**
225     * @return string
226     */
227    public function totalMediaManuscript(): string
228    {
229        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MANUSCRIPT));
230    }
231
232    /**
233     * @return string
234     */
235    public function totalMediaMap(): string
236    {
237        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_MAP));
238    }
239
240    /**
241     * @return string
242     */
243    public function totalMediaNewspaper(): string
244    {
245        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_NEWSPAPER));
246    }
247
248    /**
249     * @return string
250     */
251    public function totalMediaPainting(): string
252    {
253        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_PAINTING));
254    }
255
256    /**
257     * @return string
258     */
259    public function totalMediaPhoto(): string
260    {
261        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_PHOTO));
262    }
263
264    /**
265     * @return string
266     */
267    public function totalMediaTombstone(): string
268    {
269        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_TOMBSTONE));
270    }
271
272    /**
273     * @return string
274     */
275    public function totalMediaVideo(): string
276    {
277        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_VIDEO));
278    }
279
280    /**
281     * @return string
282     */
283    public function totalMediaOther(): string
284    {
285        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_OTHER));
286    }
287
288    /**
289     * @return string
290     */
291    public function totalMediaUnknown(): string
292    {
293        return I18N::number($this->totalMediaTypeQuery(self::MEDIA_TYPE_UNKNOWN));
294    }
295
296    /**
297     * Returns a sorted list of media types and their total counts.
298     *
299     * @param int $tot The total number of media files
300     *
301     * @return array<string,int>
302     */
303    private function getSortedMediaTypeList(int $tot): array
304    {
305        $media = [];
306        $c     = 0;
307        $max   = 0;
308
309        foreach (self::MEDIA_TYPES as $type) {
310            $count = $this->totalMediaTypeQuery($type);
311
312            if ($count > 0) {
313                $media[$type] = $count;
314
315                if ($count > $max) {
316                    $max = $count;
317                }
318
319                $c += $count;
320            }
321        }
322
323        $count = $this->totalMediaTypeQuery(self::MEDIA_TYPE_UNKNOWN);
324        if ($count > 0) {
325            $media[self::MEDIA_TYPE_UNKNOWN] = $tot - $c;
326            if ($tot - $c > $max) {
327                $max = $count;
328            }
329        }
330
331        if (count($media) > 10 && ($max / $tot) > 0.6) {
332            arsort($media);
333            $media = array_slice($media, 0, 10);
334            $c     = $tot;
335
336            foreach ($media as $cm) {
337                $c -= $cm;
338            }
339
340            if (isset($media[self::MEDIA_TYPE_OTHER])) {
341                $media[self::MEDIA_TYPE_OTHER] += $c;
342            } else {
343                $media[self::MEDIA_TYPE_OTHER] = $c;
344            }
345        }
346
347        asort($media);
348
349        return $media;
350    }
351
352    /**
353     * @param string|null $color_from
354     * @param string|null $color_to
355     *
356     * @return string
357     */
358    public function chartMedia(string $color_from = null, string $color_to = null): string
359    {
360        $tot   = $this->totalMediaTypeQuery(self::MEDIA_TYPE_ALL);
361        $media = $this->getSortedMediaTypeList($tot);
362
363        return (new ChartMedia())
364            ->chartMedia($media, $color_from, $color_to);
365    }
366}
367