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