Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 132
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 / 132
0.00% covered (danger)
0.00%
0 / 7
380
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 / 90
0.00% covered (danger)
0.00%
0 / 1
90
 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}%;' ondblclick='olz.handleRocketClick(this, event)'>{$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            $is_counting_emoji = $run->getIsCounting() ? '✅' : '🚫';
132            $is_counting_title = $run->getIsCounting() ? 'zählt' : 'zählt nicht';
133            $name = $run->getRunnerName() ?? "?";
134            $sport_type = "?";
135            if (is_array($info)) {
136                $sport_type = "{$info['type']} / {$info['sport_type']}";
137            }
138            $source = $this->anniversaryUtils()->getPrettySource($run->getSource() ?? '?');
139            $distance_km = number_format($run->getDistanceMeters() / 1000, 2);
140            $inclination_percent = $run->getDistanceMeters()
141                ? number_format($run->getElevationMeters() * 100 / $run->getDistanceMeters(), 2)
142                : 'NaN';
143            $out .= <<<ZZZZZZZZZZ
144                <tr>
145                    <td>{$date}</td>
146                    <td>{$name}</td>
147                    <td>{$source}</td>
148                    <td class='number'>{$distance_km}km</td>
149                    <td class='number'><b>{$run->getElevationMeters()}m</b></td>
150                    <td class='number'>{$inclination_percent}%</td>
151                    <td><span title='{$is_counting_title}'>{$is_counting_emoji} {$sport_type}</span></td>
152                </tr>
153                ZZZZZZZZZZ;
154        }
155        $out .= "</table></div>";
156
157        $out .= <<<ZZZZZZZZZZ
158            <h3>
159                Deine Aktivitäten (ohne Strava)
160                <button
161                    id='create-run-button'
162                    class='btn btn-secondary'
163                    onclick='return olz.initOlzEditRunModal()'
164                >
165                    <img src='{$code_href}assets/icns/new_white_16.svg' class='noborder' />
166                    Aktivität manuell hinzufügen
167                </button>
168            </h3>
169            ZZZZZZZZZZ;
170        $out .= <<<'ZZZZZZZZZZ'
171            <div class='activities-table activities-manual'>
172                <table>
173                    <tr class='header'>
174                        <td></td>
175                        <td>Datum</td>
176                        <td>Quelle</td>
177                        <td>Distanz</td>
178                        <td>Höhenmeter</td>
179                        <td>Steigung</td>
180                    </tr>
181            ZZZZZZZZZZ;
182        $runs_repo = $this->entityManager()->getRepository(RunRecord::class);
183        $runs = $runs_repo->findBy(
184            ['user' => $user, 'on_off' => 1],
185            ['run_at' => 'DESC'],
186        );
187        foreach ($runs as $run) {
188            $id = $run->getId();
189            $json_id = json_encode($id);
190            $date = $run->getRunAt()->format('d.m.Y H:i:s');
191            $edit_button = $run->getSource() === 'manuell' ? <<<ZZZZZZZZZZ
192                    <button
193                        id='edit-run-{$id}-button'
194                        class='btn btn-secondary-outline btn-sm edit-run-list-button'
195                        onclick='return olz.olzAnniversaryEditRun({$json_id})'
196                    >
197                        <img src='{$code_href}assets/icns/edit_16.svg' class='noborder' />
198                    </button>
199                ZZZZZZZZZZ : '';
200            $source = $this->anniversaryUtils()->getPrettySource($run->getSource() ?? '?');
201            $distance_km = number_format($run->getDistanceMeters() / 1000, 2);
202            $inclination_percent = number_format($run->getElevationMeters() * 100 / $run->getDistanceMeters(), 2);
203            $out .= <<<ZZZZZZZZZZ
204                <tr>
205                    <td>{$edit_button}</td>
206                    <td>{$date}</td>
207                    <td>{$source}</td>
208                    <td class='number'>{$distance_km}km</td>
209                    <td class='number'><b>{$run->getElevationMeters()}m</b></td>
210                    <td class='number'>{$inclination_percent}%</td>
211                </tr>
212                ZZZZZZZZZZ;
213        }
214        $out .= "</table></div>";
215        return $out;
216    }
217
218    protected function getElevationStravaHtml(): string {
219        $user = $this->authUtils()->getCurrentUser();
220        if (!$user || !$this->authUtils()->hasPermission('anniversary', $user)) {
221            return '';
222        }
223        $strava_link_repo = $this->entityManager()->getRepository(StravaLink::class);
224        $strava_links = $strava_link_repo->findBy(['user' => $user]);
225        $num_strava_links = count($strava_links);
226        $redirect_url = "{$this->envUtils()->getBaseHref()}{$this->envUtils()->getCodeHref()}2026";
227        $strava_url = $this->stravaUtils()->getRegistrationUrl(['read', 'activity:read'], $redirect_url);
228        $out = "<div class='admin-only'><div class='admin-only-text'>Nur für Organisatoren sichtbar</div>";
229        if ($num_strava_links === 0) {
230            $out .= "<p>😕 Kein Strava-Konto verlinkt. <a href='{$strava_url}' class='linkext'>Jetzt mit Strava verbinden!</a></p>";
231        } else {
232            $out .= "<p>✅ Du bist mit diesen {$num_strava_links} Strava-Konten verbunden:</p><ul>";
233            foreach ($strava_links as $strava_link) {
234                $athlete_id = $strava_link->getStravaUser();
235                $athlete_url = "https://www.strava.com/athletes/{$athlete_id}";
236                $date = $strava_link->getLinkedAt()?->format('d.m.Y H:i:s');
237                $out .= "<li><a href='{$athlete_url}'>{$athlete_id}</a> (erstellt: {$date})</li>";
238            }
239            $out .= "</ul>";
240            $out .= "<p><a href='{$strava_url}'>Mit Strava verbinden</a></p>";
241        }
242        $out .= '</div>';
243        return $out;
244    }
245
246    protected function getZielsprintHtml(): string {
247        $out = '<h2>🏁 Zielsprint-Challenge 🏃</h2>';
248        $out .= OlzEditableText::render(['snippet' => PredefinedSnippet::AnniversaryZielsprint]);
249        $out .= OlzZielsprint::render();
250        return $out;
251    }
252}