Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 134
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
OlzAnniversaryParams
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
OlzAnniversary
0.00% covered (danger)
0.00%
0 / 134
0.00% covered (danger)
0.00%
0 / 7
342
0.00% covered (danger)
0.00%
0 / 1
 hasAccess
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSearchTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSearchResultsWhenHasAccess
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getHtmlWhenHasAccess
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 getRunsHtml
0.00% covered (danger)
0.00%
0 / 92
0.00% covered (danger)
0.00%
0 / 1
72
 getElevationStravaHtml
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
30
 getZielsprintHtml
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Olz\Anniversary\Components\OlzAnniversary;
4
5use Doctrine\Common\Collections\Criteria;
6use Olz\Anniversary\Components\OlzAnniversaryRocket\OlzAnniversaryRocket;
7use Olz\Components\Common\OlzEditableText\OlzEditableText;
8use Olz\Components\Common\OlzRootComponent;
9use Olz\Components\OlzZielsprint\OlzZielsprint;
10use Olz\Components\Page\OlzFooter\OlzFooter;
11use Olz\Components\Page\OlzHeader\OlzHeader;
12use Olz\Entity\Anniversary\RunRecord;
13use Olz\Entity\StravaLink;
14use Olz\Repository\Snippets\PredefinedSnippet;
15use Olz\Utils\HttpParams;
16
17/** @extends HttpParams<array{}> */
18class OlzAnniversaryParams extends HttpParams {
19}
20
21/** @extends OlzRootComponent<array<string, mixed>> */
22class OlzAnniversary extends OlzRootComponent {
23    public function hasAccess(): bool {
24        return true;
25    }
26
27    public function getSearchTitle(): string {
28        return 'Jubiläumsjahr';
29    }
30
31    public function getSearchResultsWhenHasAccess(array $terms): array {
32        return [];
33    }
34
35    public static string $title = "🎉 20 Jahre OL Zimmerberg 🥳";
36    public static string $description = "Alle Aktivitäten und Informationen zum Jubiläumsjahr 2026.";
37
38    public function getHtmlWhenHasAccess(mixed $args): string {
39        $this->httpUtils()->validateGetParams(OlzAnniversaryParams::class);
40
41        $out = OlzHeader::render([
42            'title' => self::$title,
43            'description' => self::$description,
44            'norobots' => true,
45        ]);
46        $out .= <<<ZZZZZZZZZZ
47            <div class='content-full olz-anniversary'>
48                <h1>🎉 20 Jahre OL Zimmerberg 🥳</h1>
49                <br>
50                {$this->getRunsHtml()}
51                {$this->getElevationStravaHtml()}
52                <br>
53                {$this->getZielsprintHtml()}
54            </div>
55            ZZZZZZZZZZ;
56
57        $out .= OlzFooter::render();
58        return $out;
59    }
60
61    protected function getRunsHtml(): string {
62        $code_href = $this->envUtils()->getCodeHref();
63        $user = $this->authUtils()->getCurrentUser();
64        $out = '<h2>🏃 Höhenmeter-Challenge ⛰️</h2>';
65        if (!$user) {
66            $out .= "<p>😕 Du musst <a href='#login-dialog'>eingeloggt</a> sein, um an der Höhenmeter-Challenge teilzunehmen.</p>";
67            return $out;
68        }
69
70        $out .= OlzEditableText::render(['snippet' => PredefinedSnippet::AnniversaryHoehenmeter]);
71
72        $stats = $this->anniversaryUtils()->getElevationStats();
73
74        $done_wid = \number_format(max(0, $stats['completion'] * 100), 2);
75        $diff_wid = log10(abs($stats['diffDays']) + 1) * 25;
76        $rocket = OlzAnniversaryRocket::render();
77        $out .= <<<ZZZZZZZZZZ
78                <div class='elevation-stats'>
79                    <div class='done-range'></div>
80                    <div class='done-bar' style='width: {$done_wid}%;'></div>
81                    <div class='rocket test-flaky' style='left: {$done_wid}%;'>{$rocket}</div>
82                    <div class='diff-range'></div>
83                    <div class='diff-bar {$stats['diffKind']}' style='width: {$diff_wid}%;'></div>
84                    <div class='marker' style='left: 12.72%;'></div>
85                    <div class='marker' style='left: 27.42%;'></div>
86                    <div class='marker' style='left: 42.47%;'></div>
87                    <div class='marker' style='left: 50%;'></div>
88                    <div class='marker' style='left: 57.53%;'></div>
89                    <div class='marker' style='left: 72.58%;'></div>
90                    <div class='marker' style='left: 87.28%;'></div>
91                    <div class='marker-text' style='left: 12.72%;'>-1 Monat</div>
92                    <div class='marker-text' style='left: 27.42%;'>-1 Woche</div>
93                    <div class='marker-text' style='left: 42.47%;'>-1 Tag</div>
94                    <div class='marker-text' style='left: 57.53%;'>+1 Tag</div>
95                    <div class='marker-text' style='left: 72.58%;'>+1 Woche</div>
96                    <div class='marker-text' style='left: 87.28%;'>+1 Monat</div>
97                </div>
98            ZZZZZZZZZZ;
99
100        $out .= "<h3>Aktivitäten in den letzten 24 Stunden</h3>";
101        $out .= <<<'ZZZZZZZZZZ'
102            <div class='activities-table activities-24h'>
103                <table>
104                    <tr class='header'>
105                        <td>Datum</td>
106                        <td>Person</td>
107                        <td>Quelle</td>
108                        <td>Distanz</td>
109                        <td>Höhenmeter</td>
110                        <td>Steigung</td>
111                        <td>Art</td>
112                    </tr>
113            ZZZZZZZZZZ;
114        $runs_repo = $this->entityManager()->getRepository(RunRecord::class);
115        $iso_now = $this->dateUtils()->getIsoNow();
116        $minus_one_day = \DateInterval::createFromDateString("-24 hours");
117        $one_day_ago = (new \DateTime($iso_now))->add($minus_one_day);
118        $runs = $runs_repo->matching(Criteria::create()
119            ->where(Criteria::expr()->andX(
120                Criteria::expr()->gt('run_at', $one_day_ago),
121                Criteria::expr()->eq('on_off', 1),
122            ))
123            ->orderBy(['run_at' => 'DESC'])
124            ->setFirstResult(0)
125            ->setMaxResults(1000));
126        foreach ($runs as $run) {
127            $id = $run->getId();
128            $json_id = json_encode($id);
129            $date = $run->getRunAt()->format('d.m.Y H:i');
130            $info = json_decode($run->getInfo(), true) ?? null;
131            $run_user = $run->getUser();
132            $name = "?";
133            $sport_type = "?";
134            if ($run_user) {
135                $last_name = substr($run_user->getLastName(), 0, 1).".";
136                $name = "{$run_user->getFirstName()} {$last_name}";
137            } else {
138                if (is_array($info)) {
139                    $name = "{$info['athlete']['firstname']} {$info['athlete']['lastname']}";
140                    $sport_type = "{$info['type']} / {$info['sport_type']}";
141                }
142            }
143            $source_short = mb_split('-', $run->getSource())[0] ?? '?';
144            $distance_km = number_format($run->getDistanceMeters() / 1000, 2);
145            $inclination_percent = $run->getDistanceMeters()
146                ? number_format($run->getElevationMeters() * 100 / $run->getDistanceMeters(), 2)
147                : 'NaN';
148            $out .= <<<ZZZZZZZZZZ
149                <tr>
150                    <td>{$date}</td>
151                    <td>{$name}</td>
152                    <td>{$source_short}</td>
153                    <td class='number'>{$distance_km}km</td>
154                    <td class='number'><b>{$run->getElevationMeters()}m</b></td>
155                    <td class='number'>{$inclination_percent}%</td>
156                    <td>{$sport_type}</td>
157                </tr>
158                ZZZZZZZZZZ;
159        }
160        $out .= "</table></div>";
161
162        $out .= <<<ZZZZZZZZZZ
163            <h3>
164                Deine Aktivitäten (ohne Strava)
165                <button
166                    id='create-run-button'
167                    class='btn btn-secondary'
168                    onclick='return olz.initOlzEditRunModal()'
169                >
170                    <img src='{$code_href}assets/icns/new_white_16.svg' class='noborder' />
171                    Aktivität manuell hinzufügen
172                </button>
173            </h3>
174            ZZZZZZZZZZ;
175        $out .= <<<'ZZZZZZZZZZ'
176            <div class='activities-table activities-manual'>
177                <table>
178                    <tr class='header'>
179                        <td></td>
180                        <td>Datum</td>
181                        <td>Quelle</td>
182                        <td>Distanz</td>
183                        <td>Höhenmeter</td>
184                        <td>Steigung</td>
185                    </tr>
186            ZZZZZZZZZZ;
187        $runs_repo = $this->entityManager()->getRepository(RunRecord::class);
188        $runs = $runs_repo->findBy(
189            ['user' => $user, 'on_off' => 1],
190            ['run_at' => 'DESC'],
191        );
192        foreach ($runs as $run) {
193            $id = $run->getId();
194            $json_id = json_encode($id);
195            $date = $run->getRunAt()->format('d.m.Y H:i:s');
196            $edit_button = $run->getSource() === 'manuell' ? <<<ZZZZZZZZZZ
197                    <button
198                        id='edit-run-{$id}-button'
199                        class='btn btn-secondary-outline btn-sm edit-run-list-button'
200                        onclick='return olz.olzAnniversaryEditRun({$json_id})'
201                    >
202                        <img src='{$code_href}assets/icns/edit_16.svg' class='noborder' />
203                    </button>
204                ZZZZZZZZZZ : '';
205            $distance_km = number_format($run->getDistanceMeters() / 1000, 2);
206            $inclination_percent = number_format($run->getElevationMeters() * 100 / $run->getDistanceMeters(), 2);
207            $out .= <<<ZZZZZZZZZZ
208                <tr>
209                    <td>{$edit_button}</td>
210                    <td>{$date}</td>
211                    <td>{$run->getSource()}</td>
212                    <td class='number'>{$distance_km}km</td>
213                    <td class='number'><b>{$run->getElevationMeters()}m</b></td>
214                    <td class='number'>{$inclination_percent}%</td>
215                </tr>
216                ZZZZZZZZZZ;
217        }
218        $out .= "</table></div>";
219        return $out;
220    }
221
222    protected function getElevationStravaHtml(): string {
223        $user = $this->authUtils()->getCurrentUser();
224        if (!$user || !$this->authUtils()->hasPermission('anniversary', $user)) {
225            return '';
226        }
227        $strava_link_repo = $this->entityManager()->getRepository(StravaLink::class);
228        $strava_links = $strava_link_repo->findBy(['user' => $user]);
229        $num_strava_links = count($strava_links);
230        $redirect_url = "{$this->envUtils()->getBaseHref()}{$this->envUtils()->getCodeHref()}2026";
231        $strava_url = $this->stravaUtils()->getRegistrationUrl(['read', 'activity:read'], $redirect_url);
232        $out = "<div class='admin-only'><div class='admin-only-text'>Nur für Organisatoren sichtbar</div>";
233        if ($num_strava_links === 0) {
234            $out .= "<p>😕 Kein Strava-Konto verlinkt. <a href='{$strava_url}' class='linkext'>Jetzt mit Strava verbinden!</a></p>";
235        } else {
236            $out .= "<p>✅ Du bist mit diesen {$num_strava_links} Strava-Konten verbunden:</p><ul>";
237            foreach ($strava_links as $strava_link) {
238                $athlete_id = $strava_link->getStravaUser();
239                $athlete_url = "https://www.strava.com/athletes/{$athlete_id}";
240                $date = $strava_link->getLinkedAt()?->format('d.m.Y H:i:s');
241                $out .= "<li><a href='{$athlete_url}'>{$athlete_id}</a> (erstellt: {$date})</li>";
242            }
243            $out .= "</ul>";
244            $out .= "<p><a href='{$strava_url}'>Mit Strava verbinden</a></p>";
245        }
246        $out .= '</div>';
247        return $out;
248    }
249
250    protected function getZielsprintHtml(): string {
251        $out = '<h2>🏁 Zielsprint-Challenge 🏃</h2>';
252        $out .= OlzEditableText::render(['snippet' => PredefinedSnippet::AnniversaryZielsprint]);
253        $out .= OlzZielsprint::render();
254        return $out;
255    }
256}