1 <?php
declare(strict_types
=1);
4 * This file is part of the Rapsys AirBundle package.
6 * (c) RaphaΓ«l Gertz <symfony@rapsys.eu>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Rapsys\AirBundle\Repository
;
14 use Doctrine\DBAL\Types\Types
;
15 use Doctrine\ORM\Query\ResultSetMapping
;
16 use Symfony\Component\Routing\Generator\UrlGeneratorInterface
;
21 class SessionRepository
extends EntityRepository
{
23 //TODO: document utf-8 codes ?
24 //TODO: use unknown == ? symbol by default ???
25 //π<= dancer #0001f483
26 //π<= tanguera #0001f483
29 'Morning' => 'π
', #0001f305
30 'Afternoon' => 'βοΈ', #2600
31 'Evening' => 'π', #0001f307
32 'After' => 'β¨', #2728
34 'Cleary' => 'β', #2600
35 'Sunny' => 'β
', #26c5
36 'Cloudy' => 'β', #2601
37 'Winty' => 'βοΈ', #2744
38 'Rainy' => 'π', #0001f302
39 'Stormy' => 'β', #2614
41 'Euro' => 'β¬', #20ac
42 'Free' => 'πΊ', #0001f37a
43 'Hat' => 'π©' #0001f3a9
47 * Find session as array by id
49 * @param int $id The session id
50 * @param string $locale The locale
51 * @return array The session data
53 public function findOneByIdAsArray(int $id, string $locale): ?array {
60 ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS start,
62 ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS stop,
74 s.location_id AS l_id,
76 l.description AS l_description,
77 l.address AS l_address,
78 l.zipcode AS l_zipcode,
80 l.latitude AS l_latitude,
81 l.longitude AS l_longitude,
83 l.updated AS l_updated,
86 s.application_id AS a_id,
87 a.canceled AS a_canceled,
92 au.pseudonym AS au_pseudonym,
94 p.description AS p_description,
96 p.contact AS p_contact,
99 p.profile AS p_profile,
102 GREATEST(COALESCE(s.updated, 0), COALESCE(l.updated, 0), COALESCE(t.updated, 0), COALESCE(p.updated, 0), COALESCE(MAX(sa.updated), 0), COALESCE(MAX(sau.updated), 0), COALESCE(MAX(sad.updated), 0)) AS modified,
103 GROUP_CONCAT(sa.id ORDER BY sa.user_id SEPARATOR "\\n") AS sa_id,
104 GROUP_CONCAT(IFNULL(sa.score, 'NULL') ORDER BY sa.user_id SEPARATOR "\\n") AS sa_score,
105 GROUP_CONCAT(sa.created ORDER BY sa.user_id SEPARATOR "\\n") AS sa_created,
106 GROUP_CONCAT(sa.updated ORDER BY sa.user_id SEPARATOR "\\n") AS sa_updated,
107 GROUP_CONCAT(IFNULL(sa.canceled, 'NULL') ORDER BY sa.user_id SEPARATOR "\\n") AS sa_canceled,
108 GROUP_CONCAT(sa.user_id ORDER BY sa.user_id SEPARATOR "\\n") AS sau_id,
109 GROUP_CONCAT(sau.pseudonym ORDER BY sa.user_id SEPARATOR "\\n") AS sau_pseudonym
110 FROM RapsysAirBundle:Session AS s
111 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
112 JOIN RapsysAirBundle:Slot AS t ON (t.id = s.slot_id)
113 LEFT JOIN RapsysAirBundle:Application AS a ON (a.id = s.application_id)
114 LEFT JOIN RapsysAirBundle:Dance AS ad ON (ad.id = a.dance_id)
115 LEFT JOIN RapsysAirBundle:User AS au ON (au.id = a.user_id)
116 LEFT JOIN RapsysAirBundle:Snippet AS p ON (p.location_id = s.location_id AND p.user_id = a.user_id AND p.locale = :locale)
117 LEFT JOIN RapsysAirBundle:Application AS sa ON (sa.session_id = s.id)
118 LEFT JOIN RapsysAirBundle:User AS sau ON (sau.id = sa.user_id)
119 LEFT JOIN RapsysAirBundle:Dance AS sad ON (sad.id = sa.dance_id)
125 //Replace bundle entity name by table name
126 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
128 //Get result set mapping instance
129 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
130 $rsm = new ResultSetMapping();
133 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
134 $rsm->addScalarResult('id', 'id', 'integer')
135 ->addScalarResult('date', 'date', 'date')
136 ->addScalarResult('begin', 'begin', 'time')
137 ->addScalarResult('start', 'start', 'datetime')
138 ->addScalarResult('length', 'length', 'time')
139 ->addScalarResult('stop', 'stop', 'datetime')
140 ->addScalarResult('rainfall', 'rainfall', 'float')
141 ->addScalarResult('rainrisk', 'rainrisk', 'float')
142 ->addScalarResult('realfeel', 'realfeel', 'float')
143 ->addScalarResult('realfeelmin', 'realfeelmin', 'float')
144 ->addScalarResult('realfeelmax', 'realfeelmax', 'float')
145 ->addScalarResult('temperature', 'temperature', 'float')
146 ->addScalarResult('temperaturemin', 'temperaturemin', 'float')
147 ->addScalarResult('temperaturemax', 'temperaturemax', 'float')
148 ->addScalarResult('locked', 'locked', 'datetime')
149 ->addScalarResult('created', 'created', 'datetime')
150 ->addScalarResult('updated', 'updated', 'datetime')
151 ->addScalarResult('l_id', 'l_id', 'integer')
152 ->addScalarResult('l_title', 'l_title', 'string')
153 ->addScalarResult('l_description', 'l_description', 'string')
154 ->addScalarResult('l_address', 'l_address', 'string')
155 ->addScalarResult('l_zipcode', 'l_zipcode', 'string')
156 ->addScalarResult('l_city', 'l_city', 'string')
157 ->addScalarResult('l_latitude', 'l_latitude', 'float')
158 ->addScalarResult('l_longitude', 'l_longitude', 'float')
159 ->addScalarResult('l_indoor', 'l_indoor', 'boolean')
160 ->addScalarResult('l_updated', 'l_updated', 'datetime')
161 ->addScalarResult('t_id', 't_id', 'integer')
162 ->addScalarResult('t_title', 't_title', 'string')
163 ->addScalarResult('a_id', 'a_id', 'integer')
164 ->addScalarResult('a_canceled', 'a_canceled', 'datetime')
165 ->addScalarResult('ad_id', 'ad_id', 'integer')
166 ->addScalarResult('ad_name', 'ad_name', 'string')
167 ->addScalarResult('ad_type', 'ad_type', 'string')
168 ->addScalarResult('au_id', 'au_id', 'integer')
169 ->addScalarResult('au_pseudonym', 'au_pseudonym', 'string')
170 ->addScalarResult('p_id', 'p_id', 'integer')
171 ->addScalarResult('p_description', 'p_description', 'text')
172 ->addScalarResult('p_class', 'p_class', 'text')
173 ->addScalarResult('p_contact', 'p_contact', 'text')
174 ->addScalarResult('p_donate', 'p_donate', 'text')
175 ->addScalarResult('p_link', 'p_link', 'text')
176 ->addScalarResult('p_profile', 'p_profile', 'text')
177 ->addScalarResult('p_rate', 'p_rate', 'integer')
178 ->addScalarResult('p_hat', 'p_hat', 'boolean')
179 ->addScalarResult('modified', 'modified', 'datetime')
180 //XXX: is a string because of \n separator
181 ->addScalarResult('sa_id', 'sa_id', 'string')
182 //XXX: is a string because of \n separator
183 ->addScalarResult('sa_score', 'sa_score', 'string')
184 //XXX: is a string because of \n separator
185 ->addScalarResult('sa_created', 'sa_created', 'string')
186 //XXX: is a string because of \n separator
187 ->addScalarResult('sa_updated', 'sa_updated', 'string')
188 //XXX: is a string because of \n separator
189 ->addScalarResult('sa_canceled', 'sa_canceled', 'string')
190 //XXX: is a string because of \n separator
191 ->addScalarResult('sau_id', 'sau_id', 'string')
192 //XXX: is a string because of \n separator
193 ->addScalarResult('sau_pseudonym', 'sau_pseudonym', 'string')
194 ->addIndexByScalar('id');
198 ->createNativeQuery($req, $rsm)
199 ->setParameter('id', $id)
200 ->setParameter('locale', $locale)
201 ->getOneOrNullResult();
204 if ($result === null) {
210 $route = 'rapsys_air_session_view';
213 $routeParams = ['id' => $id, 'location' => $this->slugger
->slug($this->translator
->trans($result['l_title']))];
218 'date' => $result['date'],
219 'begin' => $result['begin'],
220 'start' => $result['start'],
221 'length' => $result['length'],
222 'stop' => $result['stop'],
223 'rainfall' => $result['rainfall'] !== null ? $result['rainfall'].' mm' : $result['rainfall'],
224 'rainrisk' => $result['rainrisk'] !== null ? ($result['rainrisk']*100).' %' : $result['rainrisk'],
225 'realfeel' => $result['realfeel'] !== null ? $result['realfeel'].' Β°C' : $result['realfeel'],
226 'realfeelmin' => $result['realfeelmin'] !== null ? $result['realfeelmin'].' Β°C' : $result['realfeelmin'],
227 'realfeelmax' => $result['realfeelmax'] !== null ? $result['realfeelmax'].' Β°C' : $result['realfeelmax'],
228 'temperature' => $result['temperature'] !== null ? $result['temperature'].' Β°C' : $result['temperature'],
229 'temperaturemin' => $result['temperaturemin'] !== null ? $result['temperaturemin'].' Β°C' : $result['temperaturemin'],
230 'temperaturemax' => $result['temperaturemax'] !== null ? $result['temperaturemax'].' Β°C' : $result['temperaturemax'],
231 'locked' => $result['locked'],
232 'created' => $result['created'],
233 'updated' => $result['updated'],
234 'title' => $this->translator
->trans('Session %id%', ['%id%' => $id]),
235 'modified' => $result['modified'],
236 'application' => null,
238 'id' => $result['l_id'],
239 'at' => $this->translator
->trans('at '.$result['l_title']),
240 'title' => $locationTitle = $this->translator
->trans($result['l_title']),
241 'description' => $this->translator
->trans($result['l_description']??'None'),
242 'address' => $result['l_address'],
243 'zipcode' => $result['l_zipcode'],
244 'city' => $result['l_city'],
245 'in' => $this->translator
->trans('in '.$result['l_city']),
246 'map' => $this->translator
->trans($result['l_title'].' access map'),
247 'multimap' => $this->translator
->trans($result['l_title'].' sector map'),
248 'latitude' => $result['l_latitude'],
249 'longitude' => $result['l_longitude'],
250 'indoor' => $result['l_indoor'],
251 'slug' => $routeParams['location'],
252 'link' => $this->router
->generate('rapsys_air_location_view', ['id' => $result['l_id'], 'location' => $routeParams['location']])
255 'id' => $result['t_id'],
256 'the' => $this->translator
->trans('the '.lcfirst($result['t_title'])),
257 'title' => $this->translator
->trans($result['t_title'])
260 'applications' => null
264 if (!empty($result['a_id'])) {
265 $session['application'] = [
267 'id' => $result['ad_id'],
268 'title' => $this->translator
->trans($result['ad_name'].' '.lcfirst($result['ad_type'])),
269 'name' => $this->translator
->trans($result['ad_name']),
270 'type' => $this->translator
->trans($result['ad_type']),
271 'slug' => $routeParams['dance'] = $this->slugger
->slug($this->translator
->trans($result['ad_name'].' '.lcfirst($result['ad_type']))),
272 'link' => $this->router
->generate('rapsys_air_dance_view', ['id' => $result['ad_id'], 'name' => $this->slugger
->slug($this->translator
->trans($result['ad_name'])), 'type' => $this->slugger
->slug($this->translator
->trans($result['ad_type']))])
275 'id' => $result['au_id'],
276 'by' => $this->translator
->trans('by %pseudonym%', [ '%pseudonym%' => $result['au_pseudonym'] ]),
277 'title' => $result['au_pseudonym'],
278 'slug' => $routeParams['user'] = $this->slugger
->slug($result['au_pseudonym']),
279 'link' => $result['au_id'] == 1 && $routeParams['user'] == 'milonga-raphael' ? $this->router
->generate('rapsys_air_user_milongaraphael') : $this->router
->generate('rapsys_air_user_view', ['id' => $result['au_id'], 'user' => $routeParams['user']]),
280 'contact' => $this->router
->generate('rapsys_air_contact', ['id' => $result['au_id'], 'user' => $routeParams['user']])
282 'id' => $result['a_id'],
283 'canceled' => $result['a_canceled']
288 if (!empty($result['p_id'])) {
289 $session['snippet'] = [
290 'id' => $result['p_id'],
291 'description' => $result['p_description'],
292 'class' => $result['p_class'],
293 'contact' => $result['p_contact'],
294 'donate' => $result['p_donate'],
295 'link' => $result['p_link'],
296 'profile' => $result['p_profile'],
297 'rate' => $result['p_rate'],
298 'hat' => $result['p_hat']
303 if (!empty($result['sa_id'])) {
304 //Extract applications id
305 $result['sa_id'] = explode("\n", $result['sa_id']);
306 //Extract applications score
307 //XXX: score may be null before grant or for bad behaviour, replace NULL with 'NULL' to avoid silent drop in mysql
308 $result['sa_score'] = array_map(function($v){return $v
==='NULL'?null:$v
;}, explode("\n", $result['sa_score']));
309 //Extract applications created
310 $result['sa_created'] = array_map(function($v){return new \
DateTime($v
);}, explode("\n", $result['sa_created']));
311 //Extract applications updated
312 $result['sa_updated'] = array_map(function($v){return new \
DateTime($v
);}, explode("\n", $result['sa_updated']));
313 //Extract applications canceled
314 //XXX: canceled is null before cancelation, replace NULL with 'NULL' to avoid silent drop in mysql
315 $result['sa_canceled'] = array_map(function($v){return $v
==='NULL'?null:new \
DateTime($v
);}, explode("\n", $result['sa_canceled']));
317 //Extract applications user id
318 $result['sau_id'] = explode("\n", $result['sau_id']);
319 //Extract applications user pseudonym
320 $result['sau_pseudonym'] = explode("\n", $result['sau_pseudonym']);
323 $session['applications'] = [];
325 //Iterate on each applications id
326 foreach($result['sa_id'] as $i => $sa_id) {
327 $session['applications'][$sa_id] = [
329 'score' => $result['sa_score'][$i],
330 'created' => $result['sa_created'][$i],
331 'updated' => $result['sa_updated'][$i],
332 'canceled' => $result['sa_canceled'][$i]
334 if (!empty($result['sau_id'][$i])) {
335 $session['applications'][$sa_id]['user'] = [
336 'id' => $result['sau_id'][$i],
337 'title' => $result['sau_pseudonym'][$i],
338 'slug' => $this->slugger
->slug($result['sau_pseudonym'][$i])
345 $session['link'] = $this->router
->generate($route, $routeParams);
348 $session['canonical'] = $this->router
->generate($route, $routeParams, UrlGeneratorInterface
::ABSOLUTE_URL
);
351 $session['alternates'] = [];
353 //Iterate on each locales
354 foreach($this->translator
->getFallbackLocales() as $fallback) {
358 //Set route params location
359 $routeParams['location'] = $this->slugger
->slug($this->translator
->trans($result['l_title'], [], null, $fallback));
361 //With route params dance
362 if (!empty($routeParams['dance'])) {
363 $routeParams['dance'] = $this->slugger
->slug($this->translator
->trans($result['ad_name'].' '.lcfirst($result['ad_type']), [], null, $fallback));
366 //With route params user
367 if (!empty($routeParams['user'])) {
368 $routeParams['user'] = $this->slugger
->slug($result['au_pseudonym']);
371 //With current locale
372 if ($fallback === $locale) {
373 //Set current locale title
374 $titles[$locale] = $this->translator
->trans($this->languages
[$locale]);
375 //Without current locale
377 //Iterate on other locales
378 foreach(array_diff($this->translator
->getFallbackLocales(), [$fallback]) as $other) {
379 //Set other locale title
380 $titles[$other] = $this->translator
->trans($this->languages
[$fallback], [], null, $other);
383 //Add alternates locale
384 $session['alternates'][str_replace('_', '-', $fallback)] = [
385 'absolute' => $this->router
->generate($route, ['_locale' => $fallback]+
$routeParams, UrlGeneratorInterface
::ABSOLUTE_URL
),
386 'relative' => $this->router
->generate($route, ['_locale' => $fallback]+
$routeParams),
387 'title' => implode('/', $titles),
388 'translated' => $this->translator
->trans($this->languages
[$fallback], [], null, $fallback)
392 //Add alternates shorter locale
393 if (empty($parameters['alternates'][$shortFallback = substr($fallback, 0, 2)])) {
394 //Set locale locales context
395 $session['alternates'][$shortFallback] = [
396 'absolute' => $this->router
->generate($route, ['_locale' => $fallback]+
$routeParams, UrlGeneratorInterface
::ABSOLUTE_URL
),
397 'relative' => $this->router
->generate($route, ['_locale' => $fallback]+
$routeParams),
398 'title' => implode('/', $titles),
399 'translated' => $this->translator
->trans($this->languages
[$fallback], [], null, $fallback)
409 * Find sessions as calendar array by date period
411 * @param DatePeriod $period The date period
412 * @param string $locale The locale
413 * @param ?bool $granted The session is granted
414 * @param ?float $latitude The latitude
415 * @param ?float $longitude The longitude
416 * @param ?int $userId The user id
417 * @return array The session data
420 //TODO: calculer un titre de la page intelligent ?
421 //(utiliser la liste des villes par exemple ???)
423 //TODO: finir cette merde pour toutes les pages de listing !!!
425 //TODO: compute a max updated ???
427 //TODO: Γ priori si l'userId est fourni, qu'on veut pas juste les granted et que l'userId correspond pas Γ l'utilisateur Γ qui Γ§a a Γ©tΓ© attribuΓ© et que la personne a demandΓ© la session, on a envie de lui afficher le disputed ???
428 //(ce serait mΓͺme plus un refused, voir le cas oΓΉ c'est pas encore attribuΓ©, comment Γ§a se passe ???)
430 //TODO: la gestion des sessions pas granted et contestΓ©es est Γ faire au niveau du contrΓ΄leur userView si l'user est connectΓ©
431 //On peut utiliser le système des applications, l'id user dans application et l'id utilisateur connecté pour faire la détection
432 //Γa se rΓ©sume Γ coller un $class[] = 'disputed' ou 'lost' ? sur la session ???
434 #TODO: mettre en grisΓ© les sessions pas granted
435 #TODO: mettre en highlight (violet) les sessions de la location ou de l'user si fourni
436 //TODO: penser au cas oΓΉ on est sur un utilisateur, on veut mettre
437 //TODO: faire la dΓ©tection des sΓ©ances perdues par l'utilisateur ???
438 //XXX: Γ priori Γ§a va se jouer au niveau du contrΓ΄leur oΓΉ on va setter Γ§a
439 /*} elseif (!empty($userId) && $session['au_id'] != $userId) {
440 $class[] = 'disputed';*/
443 public function findAllByPeriodAsArray(\DatePeriod
$period, string $locale, ?bool $granted = null, ?float $latitude = null, ?float $longitude = null, ?int $userId = null) {
447 //When granted is set
448 if (empty($granted)) {
449 //Set application and user as optional
456 //When latitude and longitude
457 if ($latitude !== null && $longitude !== null) {
459 //XXX: get every location between 0 and 15 km of latitude and longitude
462 FROM RapsysAirBundle:Location AS l
463 WHERE ACOS(SIN(RADIANS(:latitude))*SIN(RADIANS(l.latitude))+COS(RADIANS(:latitude))*COS(RADIANS(l.latitude))*COS(RADIANS(:longitude - l.longitude)))*40030.17/2/PI() BETWEEN 0 AND 15
466 //Replace bundle entity name by table name
467 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
469 //Get result set mapping instance
470 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
471 $rsm = new ResultSetMapping();
474 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
475 //addScalarResult($sqlColName, $resColName, $type = 'string');
476 $rsm->addScalarResult('id', 'id', 'integer')
477 ->addIndexByScalar('id');
480 //XXX: check that latitude and longitude have not be swapped !!!
481 //XXX: latitude ~= 48.x longitude ~= 2.x
482 $locationIds = array_keys(
484 ->createNativeQuery($req, $rsm)
485 ->setParameter('latitude', $latitude)
486 ->setParameter('longitude', $longitude)
490 //Add location id clause
491 $locationSql = "\n\t".'AND s.location_id IN (:lids)';
493 } elseif ($userId !== null) {
495 //XXX: get every location between 0 and 15 km
499 SELECT l.id, l.latitude, l.longitude
500 FROM RapsysAirBundle:Application AS a
501 JOIN RapsysAirBundle:Session AS s ON (s.id = a.session_id)
502 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
503 WHERE a.user_id = :id
508 JOIN RapsysAirBundle:Location AS l2
509 WHERE ACOS(SIN(RADIANS(a.latitude))*SIN(RADIANS(l2.latitude))+COS(RADIANS(a.latitude))*COS(RADIANS(l2.latitude))*COS(RADIANS(a.longitude - l2.longitude)))*40030.17/2/PI() BETWEEN 0 AND 15
514 //Replace bundle entity name by table name
515 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
517 //Get result set mapping instance
518 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
519 $rsm = new ResultSetMapping();
522 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
523 //addScalarResult($sqlColName, $resColName, $type = 'string');
524 $rsm->addScalarResult('id', 'id', 'integer')
525 ->addIndexByScalar('id');
528 $locationIds = array_keys(
530 ->createNativeQuery($req, $rsm)
531 ->setParameter('id', $userId)
536 if (!empty($locationIds)) {
537 //Add location id clause
538 $locationSql = "\n\t".'AND s.location_id IN (:lids)';
553 ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS start,
554 ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS stop,
555 s.location_id AS l_id,
557 l.zipcode AS l_zipcode,
559 l.latitude AS l_latitude,
560 l.longitude AS l_longitude,
561 l.indoor AS l_indoor,
564 s.application_id AS a_id,
565 a.canceled AS a_canceled,
570 au.pseudonym AS au_pseudonym,
573 GROUP_CONCAT(sa.user_id ORDER BY sa.user_id SEPARATOR "\\n") AS sau_id,
574 GROUP_CONCAT(sau.pseudonym ORDER BY sa.user_id SEPARATOR "\\n") AS sau_pseudonym,
575 GROUP_CONCAT(sa.dance_id ORDER BY sa.user_id SEPARATOR "\\n") AS sad_id,
576 GROUP_CONCAT(sad.name ORDER BY sa.user_id SEPARATOR "\\n") AS sad_name,
577 GROUP_CONCAT(sad.type ORDER BY sa.user_id SEPARATOR "\\n") AS sad_type,
578 GREATEST(COALESCE(s.updated, 0), COALESCE(l.updated, 0), COALESCE(p.updated, 0), COALESCE(MAX(sa.updated), 0), COALESCE(MAX(sau.updated), 0), COALESCE(MAX(sad.updated), 0)) AS modified
579 FROM RapsysAirBundle:Session AS s
580 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
581 JOIN RapsysAirBundle:Slot AS t ON (t.id = s.slot_id)
582 ${grantSql}JOIN RapsysAirBundle:Application AS a ON (a.id = s.application_id)
583 ${grantSql}JOIN RapsysAirBundle:Dance AS ad ON (ad.id = a.dance_id)
584 ${grantSql}JOIN RapsysAirBundle:User AS au ON (au.id = a.user_id)
585 LEFT JOIN RapsysAirBundle:Snippet AS p ON (p.location_id = s.location_id AND p.user_id = a.user_id AND p.locale = :locale)
586 LEFT JOIN RapsysAirBundle:Application AS sa ON (sa.session_id = s.id)
587 LEFT JOIN RapsysAirBundle:Dance AS sad ON (sad.id = sa.dance_id)
588 LEFT JOIN RapsysAirBundle:User AS sau ON (sau.id = sa.user_id)
589 WHERE s.date BETWEEN :begin AND :end${locationSql}
594 //Replace bundle entity name by table name
595 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
597 //Get result set mapping instance
598 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
599 $rsm = new ResultSetMapping();
602 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
603 //addScalarResult($sqlColName, $resColName, $type = 'string');
604 $rsm->addScalarResult('id', 'id', 'integer')
605 ->addScalarResult('date', 'date', 'date')
606 ->addScalarResult('rainrisk', 'rainrisk', 'float')
607 ->addScalarResult('rainfall', 'rainfall', 'float')
608 ->addScalarResult('realfeel', 'realfeel', 'float')
609 ->addScalarResult('temperature', 'temperature', 'float')
610 ->addScalarResult('locked', 'locked', 'datetime')
611 ->addScalarResult('start', 'start', 'datetime')
612 ->addScalarResult('stop', 'stop', 'datetime')
613 ->addScalarResult('modified', 'modified', 'datetime')
614 ->addScalarResult('t_id', 't_id', 'integer')
615 ->addScalarResult('t_title', 't_title', 'string')
616 ->addScalarResult('l_id', 'l_id', 'integer')
617 ->addScalarResult('l_title', 'l_title', 'string')
618 ->addScalarResult('l_zipcode', 'l_zipcode', 'string')
619 ->addScalarResult('l_city', 'l_city', 'string')
620 ->addScalarResult('l_latitude', 'l_latitude', 'float')
621 ->addScalarResult('l_longitude', 'l_longitude', 'float')
622 ->addScalarResult('l_indoor', 'l_indoor', 'boolean')
623 ->addScalarResult('a_id', 'a_id', 'integer')
624 ->addScalarResult('a_canceled', 'a_canceled', 'datetime')
625 ->addScalarResult('ad_id', 'ad_id', 'string')
626 ->addScalarResult('ad_name', 'ad_name', 'string')
627 ->addScalarResult('ad_type', 'ad_type', 'string')
628 ->addScalarResult('au_id', 'au_id', 'integer')
629 ->addScalarResult('au_pseudonym', 'au_pseudonym', 'string')
630 ->addScalarResult('p_rate', 'p_rate', 'integer')
631 ->addScalarResult('p_hat', 'p_hat', 'boolean')
632 //XXX: is a string because of \n separator
633 ->addScalarResult('sau_id', 'sau_id', 'string')
634 //XXX: is a string because of \n separator
635 ->addScalarResult('sau_pseudonym', 'sau_pseudonym', 'string')
636 //XXX: is a string because of \n separator
637 ->addScalarResult('sad_id', 'sad_id', 'string')
638 //XXX: is a string because of \n separator
639 ->addScalarResult('sad_name', 'sad_name', 'string')
640 //XXX: is a string because of \n separator
641 ->addScalarResult('sad_type', 'sad_type', 'string')
642 ->addIndexByScalar('id');
646 ->createNativeQuery($req, $rsm)
647 ->setParameter('begin', $period->getStartDate())
648 ->setParameter('end', $period->getEndDate())
649 ->setParameter('locale', $locale);
651 //Add optional location ids
652 if (!empty($locationIds)) {
653 $res->setParameter('lids', $locationIds);
657 $result = $res->getResult();
666 $route = 'rapsys_air_session_view';
668 //Iterate on each day
669 foreach($period as $date) {
670 //Init day in calendar
671 $calendar[$Ymd = $date->format('Ymd')] = [
672 'title' => $this->translator
->trans($date->format('l')).' '.$date->format('d'),
678 //Detect month change
679 if ($month != $date->format('m')) {
680 $month = $date->format('m');
681 //Append month for first day of month
682 //XXX: except if today to avoid double add
683 if ($date->format('U') != strtotime('today')) {
684 $calendar[$Ymd]['title'] .= '/'.$month;
688 if ($date->format('U') == ($today = strtotime('today'))) {
689 $calendar[$Ymd]['title'] .= '/'.$month;
690 $calendar[$Ymd]['current'] = true;
691 $calendar[$Ymd]['class'][] = 'current';
693 //Disable passed days
694 if ($date->format('U') < $today) {
695 $calendar[$Ymd]['disabled'] = true;
696 $calendar[$Ymd]['class'][] = 'disabled';
698 //Set next month days
699 if ($date->format('m') > date('m')) {
700 $calendar[$Ymd]['next'] = true;
701 #$calendar[$Ymd]['class'][] = 'next';
705 if ($date->format('w') == 0) {
706 $calendar[$Ymd]['class'][] = 'sunday';
709 //Iterate on each session to find the one of the day
710 foreach($result as $session) {
711 if (($sessionYmd = $session['date']->format('Ymd')) == $Ymd) {
712 //With empty or greatest modified
713 if ($calendar[$Ymd]['modified'] === null || $session['modified'] >= $calendar[$Ymd]['modified']) {
715 $calendar[$Ymd]['modified'] = $session['modified'];
719 $applications = array_combine($candidates = explode("\n", $session['sau_id']), explode("\n", $session['sau_pseudonym']));
725 if (!empty($session['locked'])) {
730 if (!empty($session['a_id'])) {
731 //With canceled session
732 if (!empty($session['a_canceled'])) {
733 $class[] = 'canceled';
734 //With disputed session
735 } elseif ($userId !== null && $session['au_id'] != $userId && !empty($candidates[$userId])) {
736 $class[] = 'disputed';
739 $class[] = 'granted';
743 if ($userId !== null && $session['au_id'] == $userId) {
744 $class[] = 'highlight';
747 $class[] = 'pending';
750 //With latitude and longitude
751 if ($latitude !== null && $longitude !== null && $session['l_latitude'] == $latitude && $session['l_longitude'] == $longitude) {
752 $class[] = 'highlight';
758 'glyph' => self
::GLYPHS
['Cleary'],
762 //Compute temperature glyph
763 //XXX: temperature may be null
764 if ($session['temperature'] >= 17 && $session['temperature'] < 24) {
765 $temperature['glyph'] = self
::GLYPHS
['Sunny'];
766 } elseif ($session['temperature'] >= 10 && $session['temperature'] < 17) {
767 $temperature['glyph'] = self
::GLYPHS
['Cloudy'];
768 } elseif ($session['temperature'] !== null && $session['temperature'] < 10) {
769 $temperature['glyph'] = self
::GLYPHS
['Winty'];
772 //Check if temperature is available
773 if ($session['temperature'] !== null) {
774 $temperature['title'][] = $session['temperature'].'Β°C';
777 //Check if realfeel is available
778 if ($session['realfeel'] !== null) {
779 $temperature['title'][] = $session['realfeel'].'Β°R';
782 //Compute temperature title
783 $temperature['title'] = implode(' ', $temperature['title']);
787 'glyph' => self
::GLYPHS
['Cleary'],
792 //XXX: rainfall and rainrisk may be null
793 if ($session['rainrisk'] > 0.50 || $session['rainfall'] > 2) {
794 $rain['glyph'] = self
::GLYPHS
['Stormy'];
795 } elseif ($session['rainrisk'] > 0.40 || $session['rainfall'] > 1) {
796 $rain['glyph'] = self
::GLYPHS
['Rainy'];
799 //Check if rainrisk is available
800 if ($session['rainrisk'] !== null) {
801 $rain['title'][] = ($session['rainrisk']*100).'%';
804 //Check if rainfall is available
805 if ($session['rainfall'] !== null) {
806 $rain['title'][] = $session['rainfall'].'mm';
810 $rain['title'] = implode(' ', $rain['title']);
819 $routeParams = ['id' => $session['id'], 'location' => $this->slugger
->slug($this->translator
->trans($session['l_title']))];
822 if (!empty($session['a_id'])) {
824 $routeParams['dance'] = $this->slugger
->slug($dance = $this->translator
->trans($session['ad_name'].' '.lcfirst($session['ad_type'])));
827 $routeParams['user'] = $this->slugger
->slug($session['au_pseudonym']);
830 $title = $this->translator
->trans('%dance% %id% by %pseudonym% %location% %city%', ['%dance%' => $dance, '%id%' => $session['id'], '%pseudonym%' => $session['au_pseudonym'], '%location%' => $this->translator
->trans('at '.$session['l_title']), '%city%' => $this->translator
->trans('in '.$session['l_city'])]);
835 'id' => $session['ad_id'],
836 'name' => $this->translator
->trans($session['ad_name']),
837 'type' => $this->translator
->trans($session['ad_type']),
841 'id' => $session['au_id'],
842 'title' => $session['au_pseudonym']
848 'glyph' => self
::GLYPHS
['Free'],
850 'title' => $this->translator
->trans('Free')
854 if (!empty($session['p_hat'])) {
856 $rate['glyph'] = self
::GLYPHS
['Hat'];
859 if (!empty($session['p_rate'])) {
861 $rate['rate'] = $session['p_rate'];
864 $rate['title'] = $this->translator
->trans('To the hat, ideally %rate% β¬', ['%rate%' => $session['p_rate']]);
868 $rate['title'] = $this->translator
->trans('To the hat');
871 } elseif (!empty($session['p_rate'])) {
873 $rate['glyph'] = self
::GLYPHS
['Euro'];
876 $rate['rate'] = $session['p_rate'];
879 $rate['title'] = $session['p_rate'].' β¬';
881 //With unique application
882 } elseif (count($applications) == 1) {
884 $dance = $this->translator
->trans($session['sad_name'].' '.lcfirst($session['sad_type']));
887 $title = $this->translator
->trans('%dance% %id% by %pseudonym% %location% %city%', ['%dance%' => $dance, '%id%' => $session['id'], '%pseudonym%' => $session['sau_pseudonym'], '%location%' => $this->translator
->trans('at '.$session['l_title']), '%city%' => $this->translator
->trans('in '.$session['l_city'])]);
892 'id' => $session['sad_id'],
893 'name' => $this->translator
->trans($session['sad_name']),
894 'type' => $this->translator
->trans($session['sad_type']),
898 'id' => $session['sau_id'],
899 'title' => $session['sau_pseudonym']
903 //TODO: glyph stuff ???
904 //Without application
907 $title = $this->translator
->trans('%slot% %id% %location%', ['%slot%' => $this->translator
->trans($session['t_title']), '%id%' => $session['id'], '%location%' => $this->translator
->trans('at '.$session['l_title'])]);
911 $calendar[$Ymd]['sessions'][$session['t_id'].sprintf('%05d', $session['id'])] = [
912 'id' => $session['id'],
913 'start' => $session['start'],
914 'stop' => $session['stop'],
916 'temperature' => $temperature,
919 'link' => $this->router
->generate($route, $routeParams),
921 'id' => $session['l_id'],
922 'title' => $this->translator
->trans($session['l_title']),
923 'latitude' => $session['l_latitude'],
924 'longitude' => $session['l_longitude'],
925 'indoor' => $session['l_indoor'],
926 'at' => $at = $this->translator
->trans('at '.$session['l_title']),
927 'in' => $in = $this->translator
->trans('in '.$session['l_city']),
928 'atin' => $at.' '.$in,
929 'city' => $session['l_city'],
930 'zipcode' => $session['l_zipcode']
932 'application' => $application,
934 'glyph' => self
::GLYPHS
[$session['t_title']],
935 'title' => $this->translator
->trans($session['t_title'])
938 'applications' => $applications
944 ksort($calendar[$Ymd]['sessions']);
952 * Find session by location, slot and date
954 * @param $location The location
955 * @param $slot The slot
956 * @param $date The datetime
958 public function findOneByLocationSlotDate($location, $slot, $date) {
960 return $this->getEntityManager()
961 ->createQuery('SELECT s FROM RapsysAirBundle:Session s WHERE (s.location = :location AND s.slot = :slot AND s.date = :date)')
962 ->setParameter('location', $location)
963 ->setParameter('slot', $slot)
964 ->setParameter('date', $date)
969 * Find sessions by date period
971 * @param $period The date period
973 public function findAllByDatePeriod($period) {
975 return $this->getEntityManager()
976 ->createQuery('SELECT s FROM RapsysAirBundle:Session s WHERE s.date BETWEEN :begin AND :end')
977 ->setParameter('begin', $period->getStartDate())
978 ->setParameter('end', $period->getEndDate())
983 * Find sessions by location and date period
985 * @param $location The location
986 * @param $period The date period
988 public function findAllByLocationDatePeriod($location, $period) {
990 return $this->getEntityManager()
991 ->createQuery('SELECT s FROM RapsysAirBundle:Session s WHERE (s.location = :location AND s.date BETWEEN :begin AND :end)')
992 ->setParameter('location', $location)
993 ->setParameter('begin', $period->getStartDate())
994 ->setParameter('end', $period->getEndDate())
999 * Find one session by location and user id within last month
1001 * @param $location The location id
1002 * @param $user The user id
1004 public function findOneWithinLastMonthByLocationUser($location, $user) {
1006 //XXX: give the gooddelay to guest just in case
1009 FROM RapsysAirBundle:Session s
1010 JOIN RapsysAirBundle:Application a ON (a.id = s.application_id AND a.user_id = :uid AND (a.canceled IS NULL OR TIMESTAMPDIFF(DAY, a.canceled, ADDTIME(s.date, s.begin)) < 1))
1011 WHERE s.location_id = :lid AND s.date >= DATE_ADD(DATE_SUB(NOW(), INTERVAL 1 MONTH), INTERVAL :gooddelay DAY)
1014 //Replace bundle entity name by table name
1015 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
1017 //Get result set mapping instance
1018 $rsm = new ResultSetMapping();
1020 //Declare all fields
1021 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
1022 $rsm->addScalarResult('id', 'id', 'integer')
1023 ->addIndexByScalar('id');
1027 ->createNativeQuery($req, $rsm)
1028 ->setParameter('lid', $location)
1029 ->setParameter('uid', $user)
1030 ->setParameter('gooddelay', self
::SENIOR_DELAY
)
1031 ->getOneOrNullResult();
1035 * Fetch sessions by date period
1037 * @param $period The date period
1038 * @param $locale The locale
1040 public function fetchAllByDatePeriod($period, $locale = null) {
1042 //TODO: exclude opera and others ?
1049 ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS start,
1050 ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS stop,
1051 s.location_id AS l_id,
1052 l.address AS l_address,
1053 l.zipcode AS l_zipcode,
1056 l.description AS l_description,
1057 l.latitude AS l_latitude,
1058 l.longitude AS l_longitude,
1059 s.application_id AS a_id,
1060 a.canceled AS a_canceled,
1064 au.forename AS au_forename,
1065 au.pseudonym AS au_pseudonym,
1067 p.description AS p_description,
1072 p.contact AS p_contact,
1073 p.donate AS p_donate,
1075 p.profile AS p_profile
1076 FROM RapsysAirBundle:Session AS s
1077 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
1078 JOIN RapsysAirBundle:Application AS a ON (a.id = s.application_id)
1079 JOIN RapsysAirBundle:Dance AS ad ON (ad.id = a.dance_id)
1080 JOIN RapsysAirBundle:User AS au ON (au.id = a.user_id)
1081 LEFT JOIN RapsysAirBundle:Snippet AS p ON (p.location_id = s.location_id AND p.user_id = a.user_id AND p.locale = :locale)
1082 WHERE s.date BETWEEN :begin AND :end
1086 //Replace bundle entity name by table name
1087 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
1089 //Get result set mapping instance
1090 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
1091 $rsm = new ResultSetMapping();
1093 //Declare all fields
1094 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
1095 //addScalarResult($sqlColName, $resColName, $type = 'string');
1096 $rsm->addScalarResult('id', 'id', 'integer')
1097 ->addScalarResult('date', 'date', 'date')
1098 ->addScalarResult('locked', 'locked', 'datetime')
1099 ->addScalarResult('updated', 'updated', 'datetime')
1100 ->addScalarResult('start', 'start', 'datetime')
1101 ->addScalarResult('stop', 'stop', 'datetime')
1102 ->addScalarResult('l_id', 'l_id', 'integer')
1103 ->addScalarResult('l_address', 'l_address', 'string')
1104 ->addScalarResult('l_zipcode', 'l_zipcode', 'string')
1105 ->addScalarResult('l_city', 'l_city', 'string')
1106 ->addScalarResult('l_latitude', 'l_latitude', 'float')
1107 ->addScalarResult('l_longitude', 'l_longitude', 'float')
1108 ->addScalarResult('l_title', 'l_title', 'string')
1109 ->addScalarResult('l_description', 'l_description', 'string')
1110 ->addScalarResult('t_id', 't_id', 'integer')
1111 ->addScalarResult('t_title', 't_title', 'string')
1112 ->addScalarResult('a_id', 'a_id', 'integer')
1113 ->addScalarResult('a_canceled', 'a_canceled', 'datetime')
1114 ->addScalarResult('ad_name', 'ad_name', 'string')
1115 ->addScalarResult('ad_type', 'ad_type', 'string')
1116 ->addScalarResult('au_id', 'au_id', 'integer')
1117 ->addScalarResult('au_forename', 'au_forename', 'string')
1118 ->addScalarResult('au_pseudonym', 'au_pseudonym', 'string')
1119 ->addScalarResult('p_id', 'p_id', 'integer')
1120 ->addScalarResult('p_description', 'p_description', 'string')
1121 ->addScalarResult('p_class', 'p_class', 'string')
1122 ->addScalarResult('p_short', 'p_short', 'string')
1123 ->addScalarResult('p_hat', 'p_hat', 'integer')
1124 ->addScalarResult('p_rate', 'p_rate', 'integer')
1125 ->addScalarResult('p_contact', 'p_contact', 'string')
1126 ->addScalarResult('p_donate', 'p_donate', 'string')
1127 ->addScalarResult('p_link', 'p_link', 'string')
1128 ->addScalarResult('p_profile', 'p_profile', 'string')
1129 ->addIndexByScalar('id');
1133 ->createNativeQuery($req, $rsm)
1134 ->setParameter('begin', $period->getStartDate())
1135 ->setParameter('end', $period->getEndDate())
1136 ->setParameter('locale', $locale);
1139 return $res->getResult();
1143 * Fetch session by id
1145 * @todo drop fetchOneById when migration is complete
1146 * @todo FIX the updated computation, we can drop the group_concat for a MAX()
1147 * @XXX DO NOT ADD a.updated as we already compute that with applications !!!
1149 * @param $id The session id
1150 * @param $locale The locale
1151 * @return array The session data
1153 public function fetchOneById($id, $locale = null) {
1155 //TODO: compute scores ?
1156 //TODO: compute delivery date ? (J-3/J-4 ?)
1162 ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS start,
1164 ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS stop,
1176 s.location_id AS l_id,
1178 l.description AS l_description,
1179 l.address AS l_address,
1180 l.zipcode AS l_zipcode,
1182 l.latitude AS l_latitude,
1183 l.longitude AS l_longitude,
1184 l.indoor AS l_indoor,
1185 l.updated AS l_updated,
1188 t.updated AS t_updated,
1189 s.application_id AS a_id,
1190 a.canceled AS a_canceled,
1191 a.dance_id AS ad_id,
1194 ad.updated AS ad_updated,
1196 au.pseudonym AS au_pseudonym,
1198 p.description AS p_description,
1200 p.contact AS p_contact,
1201 p.donate AS p_donate,
1203 p.profile AS p_profile,
1206 p.updated AS p_updated,
1207 GROUP_CONCAT(sa.id ORDER BY sa.user_id SEPARATOR "\\n") AS sa_id,
1208 GROUP_CONCAT(IFNULL(sa.score, 'NULL') ORDER BY sa.user_id SEPARATOR "\\n") AS sa_score,
1209 GROUP_CONCAT(sa.created ORDER BY sa.user_id SEPARATOR "\\n") AS sa_created,
1210 GROUP_CONCAT(sa.updated ORDER BY sa.user_id SEPARATOR "\\n") AS sa_updated,
1211 GROUP_CONCAT(IFNULL(sa.canceled, 'NULL') ORDER BY sa.user_id SEPARATOR "\\n") AS sa_canceled,
1212 GROUP_CONCAT(sa.user_id ORDER BY sa.user_id SEPARATOR "\\n") AS sau_id,
1213 GROUP_CONCAT(sau.pseudonym ORDER BY sa.user_id SEPARATOR "\\n") AS sau_pseudonym
1214 FROM RapsysAirBundle:Session AS s
1215 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
1216 JOIN RapsysAirBundle:Slot AS t ON (t.id = s.slot_id)
1217 LEFT JOIN RapsysAirBundle:Application AS a ON (a.id = s.application_id)
1218 LEFT JOIN RapsysAirBundle:Dance AS ad ON (ad.id = a.dance_id)
1219 LEFT JOIN RapsysAirBundle:User AS au ON (au.id = a.user_id)
1220 LEFT JOIN RapsysAirBundle:Snippet AS p ON (p.location_id = s.location_id AND p.user_id = a.user_id AND p.locale = :locale)
1221 LEFT JOIN RapsysAirBundle:Application AS sa ON (sa.session_id = s.id)
1222 LEFT JOIN RapsysAirBundle:User AS sau ON (sau.id = sa.user_id)
1228 //Replace bundle entity name by table name
1229 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
1231 //Get result set mapping instance
1232 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
1233 $rsm = new ResultSetMapping();
1235 //Declare all fields
1236 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
1237 $rsm->addScalarResult('id', 'id', 'integer')
1238 ->addScalarResult('date', 'date', 'date')
1239 ->addScalarResult('begin', 'begin', 'time')
1240 ->addScalarResult('start', 'start', 'datetime')
1241 ->addScalarResult('length', 'length', 'time')
1242 ->addScalarResult('stop', 'stop', 'datetime')
1243 ->addScalarResult('rainfall', 'rainfall', 'float')
1244 ->addScalarResult('rainrisk', 'rainrisk', 'float')
1245 ->addScalarResult('realfeel', 'realfeel', 'float')
1246 ->addScalarResult('realfeelmin', 'realfeelmin', 'float')
1247 ->addScalarResult('realfeelmax', 'realfeelmax', 'float')
1248 ->addScalarResult('temperature', 'temperature', 'float')
1249 ->addScalarResult('temperaturemin', 'temperaturemin', 'float')
1250 ->addScalarResult('temperaturemax', 'temperaturemax', 'float')
1251 ->addScalarResult('locked', 'locked', 'datetime')
1252 ->addScalarResult('created', 'created', 'datetime')
1253 ->addScalarResult('updated', 'updated', 'datetime')
1254 ->addScalarResult('l_id', 'l_id', 'integer')
1255 ->addScalarResult('l_title', 'l_title', 'string')
1256 ->addScalarResult('l_description', 'l_description', 'string')
1257 ->addScalarResult('l_address', 'l_address', 'string')
1258 ->addScalarResult('l_zipcode', 'l_zipcode', 'string')
1259 ->addScalarResult('l_city', 'l_city', 'string')
1260 ->addScalarResult('l_latitude', 'l_latitude', 'float')
1261 ->addScalarResult('l_longitude', 'l_longitude', 'float')
1262 ->addScalarResult('l_indoor', 'l_indoor', 'boolean')
1263 ->addScalarResult('l_updated', 'l_updated', 'datetime')
1264 ->addScalarResult('t_id', 't_id', 'integer')
1265 ->addScalarResult('t_title', 't_title', 'string')
1266 ->addScalarResult('t_updated', 't_updated', 'datetime')
1267 ->addScalarResult('a_id', 'a_id', 'integer')
1268 ->addScalarResult('a_canceled', 'a_canceled', 'datetime')
1269 ->addScalarResult('ad_id', 'ad_id', 'integer')
1270 ->addScalarResult('ad_name', 'ad_name', 'string')
1271 ->addScalarResult('ad_type', 'ad_type', 'string')
1272 ->addScalarResult('ad_updated', 'ad_updated', 'datetime')
1273 ->addScalarResult('au_id', 'au_id', 'integer')
1274 ->addScalarResult('au_pseudonym', 'au_pseudonym', 'string')
1275 ->addScalarResult('p_id', 'p_id', 'integer')
1276 ->addScalarResult('p_description', 'p_description', 'text')
1277 ->addScalarResult('p_class', 'p_class', 'text')
1278 ->addScalarResult('p_contact', 'p_contact', 'text')
1279 ->addScalarResult('p_donate', 'p_donate', 'text')
1280 ->addScalarResult('p_link', 'p_link', 'text')
1281 ->addScalarResult('p_profile', 'p_profile', 'text')
1282 ->addScalarResult('p_rate', 'p_rate', 'integer')
1283 ->addScalarResult('p_hat', 'p_hat', 'boolean')
1284 ->addScalarResult('p_updated', 'p_updated', 'datetime')
1285 //XXX: is a string because of \n separator
1286 ->addScalarResult('sa_id', 'sa_id', 'string')
1287 //XXX: is a string because of \n separator
1288 ->addScalarResult('sa_score', 'sa_score', 'string')
1289 //XXX: is a string because of \n separator
1290 ->addScalarResult('sa_created', 'sa_created', 'string')
1291 //XXX: is a string because of \n separator
1292 ->addScalarResult('sa_updated', 'sa_updated', 'string')
1293 //XXX: is a string because of \n separator
1294 ->addScalarResult('sa_canceled', 'sa_canceled', 'string')
1295 //XXX: is a string because of \n separator
1296 ->addScalarResult('sau_id', 'sau_id', 'string')
1297 //XXX: is a string because of \n separator
1298 ->addScalarResult('sau_pseudonym', 'sau_pseudonym', 'string')
1299 ->addIndexByScalar('id');
1303 ->createNativeQuery($req, $rsm)
1304 ->setParameter('id', $id)
1305 ->setParameter('locale', $locale)
1306 ->getOneOrNullResult();
1310 * Fetch sessions calendar with translated location by date period
1312 * @param $period The date period
1313 * @param $locationId The location id
1314 * @param $sessionId The session id
1315 * @param $granted The session is granted
1317 public function fetchCalendarByDatePeriod($period, $locationId = null, $sessionId = null, $granted = false, $locale = null) {
1321 //When granted is set
1322 if (empty($granted)) {
1323 //Set application and user as optional
1324 $grantSql = 'LEFT ';
1330 //When location id is set
1331 if (!empty($locationId)) {
1332 //Add location id clause
1333 $locationSql = "\n\t".'AND s.location_id = :lid';
1347 ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS start,
1348 ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS stop,
1349 s.location_id AS l_id,
1353 s.application_id AS a_id,
1354 a.canceled AS a_canceled,
1358 au.pseudonym AS au_pseudonym,
1361 GROUP_CONCAT(sa.user_id ORDER BY sa.user_id SEPARATOR "\\n") AS sau_id,
1362 GROUP_CONCAT(sau.pseudonym ORDER BY sa.user_id SEPARATOR "\\n") AS sau_pseudonym
1363 FROM RapsysAirBundle:Session AS s
1364 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
1365 JOIN RapsysAirBundle:Slot AS t ON (t.id = s.slot_id)
1366 ${grantSql}JOIN RapsysAirBundle:Application AS a ON (a.id = s.application_id)
1367 ${grantSql}JOIN RapsysAirBundle:Dance AS ad ON (ad.id = a.dance_id)
1368 ${grantSql}JOIN RapsysAirBundle:User AS au ON (au.id = a.user_id)
1369 LEFT JOIN RapsysAirBundle:Application AS sa ON (sa.session_id = s.id)
1370 LEFT JOIN RapsysAirBundle:Snippet AS p ON (p.location_id = s.location_id AND p.user_id = a.user_id AND p.locale = :locale)
1371 LEFT JOIN RapsysAirBundle:User AS sau ON (sau.id = sa.user_id)
1372 WHERE s.date BETWEEN :begin AND :end${locationSql}
1377 //Replace bundle entity name by table name
1378 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
1380 //Get result set mapping instance
1381 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
1382 $rsm = new ResultSetMapping();
1384 //Declare all fields
1385 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
1386 //addScalarResult($sqlColName, $resColName, $type = 'string');
1387 $rsm->addScalarResult('id', 'id', 'integer')
1388 ->addScalarResult('date', 'date', 'date')
1389 ->addScalarResult('rainrisk', 'rainrisk', 'float')
1390 ->addScalarResult('rainfall', 'rainfall', 'float')
1391 ->addScalarResult('realfeel', 'realfeel', 'float')
1392 ->addScalarResult('temperature', 'temperature', 'float')
1393 ->addScalarResult('locked', 'locked', 'datetime')
1394 ->addScalarResult('start', 'start', 'datetime')
1395 ->addScalarResult('stop', 'stop', 'datetime')
1396 ->addScalarResult('t_id', 't_id', 'integer')
1397 ->addScalarResult('t_title', 't_title', 'string')
1398 ->addScalarResult('l_id', 'l_id', 'integer')
1399 ->addScalarResult('l_title', 'l_title', 'string')
1400 ->addScalarResult('a_id', 'a_id', 'integer')
1401 ->addScalarResult('a_canceled', 'a_canceled', 'datetime')
1402 ->addScalarResult('ad_name', 'ad_name', 'string')
1403 ->addScalarResult('ad_type', 'ad_type', 'string')
1404 ->addScalarResult('au_id', 'au_id', 'integer')
1405 ->addScalarResult('au_pseudonym', 'au_pseudonym', 'string')
1406 ->addScalarResult('p_rate', 'p_rate', 'integer')
1407 ->addScalarResult('p_hat', 'p_hat', 'boolean')
1408 //XXX: is a string because of \n separator
1409 ->addScalarResult('sau_id', 'sau_id', 'string')
1410 //XXX: is a string because of \n separator
1411 ->addScalarResult('sau_pseudonym', 'sau_pseudonym', 'string')
1412 ->addIndexByScalar('id');
1416 ->createNativeQuery($req, $rsm)
1417 ->setParameter('begin', $period->getStartDate())
1418 ->setParameter('end', $period->getEndDate())
1419 ->setParameter('locale', $locale);
1421 //Add optional location id
1422 if (!empty($locationId)) {
1423 $res->setParameter('lid', $locationId);
1427 $res = $res->getResult();
1435 //Iterate on each day
1436 foreach($period as $date) {
1437 //Init day in calendar
1438 $calendar[$Ymd = $date->format('Ymd')] = [
1439 'title' => $this->translator
->trans($date->format('l')).' '.$date->format('d'),
1444 //Detect month change
1445 if ($month != $date->format('m')) {
1446 $month = $date->format('m');
1447 //Append month for first day of month
1448 //XXX: except if today to avoid double add
1449 if ($date->format('U') != strtotime('today')) {
1450 $calendar[$Ymd]['title'] .= '/'.$month;
1454 if ($date->format('U') == ($today = strtotime('today'))) {
1455 $calendar[$Ymd]['title'] .= '/'.$month;
1456 $calendar[$Ymd]['current'] = true;
1457 $calendar[$Ymd]['class'][] = 'current';
1459 //Disable passed days
1460 if ($date->format('U') < $today) {
1461 $calendar[$Ymd]['disabled'] = true;
1462 $calendar[$Ymd]['class'][] = 'disabled';
1464 //Set next month days
1465 if ($date->format('m') > date('m')) {
1466 $calendar[$Ymd]['next'] = true;
1467 #$calendar[$Ymd]['class'][] = 'next';
1471 if ($date->format('w') == 0) {
1472 $calendar[$Ymd]['class'][] = 'sunday';
1475 //Iterate on each session to find the one of the day
1476 foreach($res as $session) {
1477 if (($sessionYmd = $session['date']->format('Ymd')) == $Ymd) {
1478 //Count number of application
1479 $count = count(explode("\n", $session['sau_id']));
1483 if (!empty($session['a_id'])) {
1484 $applications = [ $session['au_id'] => $session['au_pseudonym'] ];
1485 if (!empty($session['a_canceled'])) {
1486 $class[] = 'canceled';
1488 $class[] = 'granted';
1490 } elseif ($count > 1) {
1491 $class[] = 'disputed';
1492 } elseif (!empty($session['locked'])) {
1493 $class[] = 'locked';
1495 $class[] = 'pending';
1498 if ($sessionId == $session['id']) {
1499 $class[] = 'highlight';
1503 //XXX: realfeel may be null, temperature should not
1504 $temperature = $session['realfeel'] !== null ? $session['realfeel'] : $session['temperature'];
1507 //XXX: rainfall may be null
1508 if ($session['rainrisk'] > 0.50 || $session['rainfall'] > 2) {
1509 $weather = self
::GLYPHS
['Stormy'];
1510 } elseif ($session['rainrisk'] > 0.40 || $session['rainfall'] > 1) {
1511 $weather = self
::GLYPHS
['Rainy'];
1512 } elseif ($temperature > 24) {
1513 $weather = self
::GLYPHS
['Cleary'];
1514 } elseif ($temperature > 17) {
1515 $weather = self
::GLYPHS
['Sunny'];
1516 } elseif ($temperature > 10) {
1517 $weather = self
::GLYPHS
['Cloudy'];
1518 } elseif ($temperature !== null) {
1519 $weather = self
::GLYPHS
['Winty'];
1527 //Check if realfeel is available
1528 if ($session['realfeel'] !== null) {
1529 $weathertitle[] = $session['realfeel'].'Β°R';
1532 //Check if temperature is available
1533 if ($session['temperature'] !== null) {
1534 $weathertitle[] = $session['temperature'].'Β°C';
1537 //Check if rainrisk is available
1538 if ($session['rainrisk'] !== null) {
1539 $weathertitle[] = ($session['rainrisk']*100).'%';
1542 //Check if rainfall is available
1543 if ($session['rainfall'] !== null) {
1544 $weathertitle[] = $session['rainfall'].'mm';
1549 0 => $this->translator
->trans($session['t_title']).' '.$this->translator
->trans('at '.$session['l_title']).$this->translator
->trans(':')
1552 //Fetch pseudonyms from session applications
1553 $applications +
= array_combine(explode("\n", $session['sau_id']), array_map(function ($v) {return '- '.$v
;}, explode("\n", $session['sau_pseudonym'])));
1561 //Check that session is not granted
1562 if (empty($session['a_id'])) {
1563 //With location id and unique application
1565 //Set unique application pseudonym
1566 $pseudonym = $session['sau_pseudonym'];
1568 //Session is granted
1570 //Replace granted application
1571 $applications[$session['au_id']] = '* '.$session['au_pseudonym'];
1574 $dance = $this->translator
->trans($session['ad_name'].' '.lcfirst($session['ad_type']));
1577 $pseudonym = $session['au_pseudonym'].($count > 1 ? ' ['.$count.']':'');
1581 $calendar[$Ymd]['sessions'][$session['t_id'].sprintf('%05d', $session['id'])] = [
1582 'id' => $session['id'],
1583 'start' => $session['start'],
1584 'stop' => $session['stop'],
1585 'location' => $this->translator
->trans($session['l_title']),
1587 'pseudonym' => $pseudonym,
1589 'slot' => self
::GLYPHS
[$session['t_title']],
1590 'slottitle' => $this->translator
->trans($session['t_title']),
1591 'weather' => $weather,
1592 'weathertitle' => implode(' ', $weathertitle),
1593 'applications' => $applications,
1594 'rate' => $session['p_rate'],
1595 'hat' => $session['p_hat']
1601 ksort($calendar[$Ymd]['sessions']);
1609 * Fetch sessions calendar with translated location by date period and user
1611 * @param $period The date period
1612 * @param $userId The user id
1613 * @param $sessionId The session id
1615 public function fetchUserCalendarByDatePeriod($period, $userId = null, $sessionId = null, $locale = null) {
1617 $userJoinSql = $userWhereSql = '';
1619 //When user id is set
1620 if (!empty($userId)) {
1622 $userJoinSql = 'JOIN RapsysAirBundle:Application AS sua ON (sua.session_id = s.id)'."\n";
1623 //Add user id clause
1624 $userWhereSql = "\n\t".'AND sua.user_id = :uid';
1628 //TODO: change as_u_* in sau_*, a_u_* in au_*, etc, see request up
1638 ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS start,
1639 ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) AS stop,
1640 s.location_id AS l_id,
1644 s.application_id AS a_id,
1648 au.pseudonym AS au_pseudonym,
1651 GROUP_CONCAT(sa.user_id ORDER BY sa.user_id SEPARATOR "\\n") AS sau_id,
1652 GROUP_CONCAT(CONCAT("- ", sau.pseudonym) ORDER BY sa.user_id SEPARATOR "\\n") AS sau_pseudonym
1653 FROM RapsysAirBundle:Session AS s
1654 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
1655 JOIN RapsysAirBundle:Slot AS t ON (t.id = s.slot_id)
1656 ${userJoinSql}LEFT JOIN RapsysAirBundle:Application AS a ON (a.id = s.application_id)
1657 LEFT JOIN RapsysAirBundle:Snippet AS p ON (p.location_id = s.location_id AND p.user_id = a.user_id AND p.locale = :locale)
1658 LEFT JOIN RapsysAirBundle:Dance AS ad ON (ad.id = a.dance_id)
1659 LEFT JOIN RapsysAirBundle:User AS au ON (au.id = a.user_id)
1660 LEFT JOIN RapsysAirBundle:Application AS sa ON (sa.session_id = s.id)
1661 LEFT JOIN RapsysAirBundle:User AS sau ON (sau.id = sa.user_id)
1662 WHERE s.date BETWEEN :begin AND :end${userWhereSql}
1667 //Replace bundle entity name by table name
1668 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
1670 //Get result set mapping instance
1671 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
1672 $rsm = new ResultSetMapping();
1674 //Declare all fields
1675 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
1676 //addScalarResult($sqlColName, $resColName, $type = 'string');
1677 $rsm->addScalarResult('id', 'id', 'integer')
1678 ->addScalarResult('date', 'date', 'date')
1679 ->addScalarResult('rainrisk', 'rainrisk', 'float')
1680 ->addScalarResult('rainfall', 'rainfall', 'float')
1681 ->addScalarResult('realfeel', 'realfeel', 'float')
1682 ->addScalarResult('temperature', 'temperature', 'float')
1683 ->addScalarResult('locked', 'locked', 'datetime')
1684 ->addScalarResult('start', 'start', 'datetime')
1685 ->addScalarResult('stop', 'stop', 'datetime')
1686 ->addScalarResult('t_id', 't_id', 'integer')
1687 ->addScalarResult('t_title', 't_title', 'string')
1688 ->addScalarResult('l_id', 'l_id', 'integer')
1689 ->addScalarResult('l_title', 'l_title', 'string')
1690 ->addScalarResult('a_id', 'a_id', 'integer')
1691 ->addScalarResult('ad_name', 'ad_name', 'string')
1692 ->addScalarResult('ad_type', 'ad_type', 'string')
1693 ->addScalarResult('au_id', 'au_id', 'integer')
1694 ->addScalarResult('au_pseudonym', 'au_pseudonym', 'string')
1695 ->addScalarResult('p_rate', 'p_rate', 'integer')
1696 ->addScalarResult('p_hat', 'p_hat', 'boolean')
1697 //XXX: is a string because of \n separator
1698 ->addScalarResult('sau_id', 'sau_id', 'string')
1699 //XXX: is a string because of \n separator
1700 ->addScalarResult('sau_pseudonym', 'sau_pseudonym', 'string')
1701 ->addIndexByScalar('id');
1705 ->createNativeQuery($req, $rsm)
1706 ->setParameter('begin', $period->getStartDate())
1707 ->setParameter('end', $period->getEndDate())
1708 ->setParameter('uid', $userId)
1709 ->setParameter('locale', $locale)
1718 //Iterate on each day
1719 foreach($period as $date) {
1720 //Init day in calendar
1721 $calendar[$Ymd = $date->format('Ymd')] = [
1722 'title' => $this->translator
->trans($date->format('l')).' '.$date->format('d'),
1727 //Detect month change
1728 if ($month != $date->format('m')) {
1729 $month = $date->format('m');
1730 //Append month for first day of month
1731 //XXX: except if today to avoid double add
1732 if ($date->format('U') != strtotime('today')) {
1733 $calendar[$Ymd]['title'] .= '/'.$month;
1737 if ($date->format('U') == ($today = strtotime('today'))) {
1738 $calendar[$Ymd]['title'] .= '/'.$month;
1739 $calendar[$Ymd]['current'] = true;
1740 $calendar[$Ymd]['class'][] = 'current';
1742 //Disable passed days
1743 if ($date->format('U') < $today) {
1744 $calendar[$Ymd]['disabled'] = true;
1745 $calendar[$Ymd]['class'][] = 'disabled';
1747 //Set next month days
1748 if ($date->format('m') > date('m')) {
1749 $calendar[$Ymd]['next'] = true;
1750 #$calendar[$Ymd]['class'][] = 'next';
1754 if ($date->format('w') == 0) {
1755 $calendar[$Ymd]['class'][] = 'sunday';
1758 //Iterate on each session to find the one of the day
1759 foreach($res as $session) {
1760 if (($sessionYmd = $session['date']->format('Ymd')) == $Ymd) {
1761 //Count number of application
1762 $count = count(explode("\n", $session['sau_id']));
1766 if (!empty($session['a_id'])) {
1767 $applications = [ $session['au_id'] => $session['au_pseudonym'] ];
1768 if ($session['au_id'] == $userId) {
1769 $class[] = 'granted';
1771 $class[] = 'disputed';
1773 } elseif ($count > 1) {
1774 $class[] = 'disputed';
1775 } elseif (!empty($session['locked'])) {
1776 $class[] = 'locked';
1778 $class[] = 'pending';
1781 if ($sessionId == $session['id']) {
1782 $class[] = 'highlight';
1786 //XXX: realfeel may be null, temperature should not
1787 $temperature = $session['realfeel'] !== null ? $session['realfeel'] : $session['temperature'];
1790 //XXX: rainfall may be null
1791 if ($session['rainrisk'] > 0.50 || $session['rainfall'] > 2) {
1792 $weather = self
::GLYPHS
['Stormy'];
1793 } elseif ($session['rainrisk'] > 0.40 || $session['rainfall'] > 1) {
1794 $weather = self
::GLYPHS
['Rainy'];
1795 } elseif ($temperature > 24) {
1796 $weather = self
::GLYPHS
['Cleary'];
1797 } elseif ($temperature > 17) {
1798 $weather = self
::GLYPHS
['Sunny'];
1799 } elseif ($temperature > 10) {
1800 $weather = self
::GLYPHS
['Cloudy'];
1801 } elseif ($temperature !== null) {
1802 $weather = self
::GLYPHS
['Winty'];
1810 //Check if realfeel is available
1811 if ($session['realfeel'] !== null) {
1812 $weathertitle[] = $session['realfeel'].'Β°R';
1815 //Check if temperature is available
1816 if ($session['temperature'] !== null) {
1817 $weathertitle[] = $session['temperature'].'Β°C';
1820 //Check if rainrisk is available
1821 if ($session['rainrisk'] !== null) {
1822 $weathertitle[] = ($session['rainrisk']*100).'%';
1825 //Check if rainfall is available
1826 if ($session['rainfall'] !== null) {
1827 $weathertitle[] = $session['rainfall'].'mm';
1832 0 => $this->translator
->trans($session['t_title']).' '.$this->translator
->trans('at '.$session['l_title']).$this->translator
->trans(':')
1835 //Fetch pseudonyms from session applications
1836 $applications +
= array_combine(explode("\n", $session['sau_id']), array_map(function ($v) {return '- '.$v
;}, explode("\n", $session['sau_pseudonym'])));
1844 //Check that session is not granted
1845 if (empty($session['a_id'])) {
1846 //With location id and unique application
1848 //Set unique application pseudonym
1849 $pseudonym = $session['sau_pseudonym'];
1851 //Session is granted
1853 //Replace granted application
1854 $applications[$session['au_id']] = '* '.$session['au_pseudonym'];
1857 $dance = $this->translator
->trans($session['ad_name'].' '.lcfirst($session['ad_type']));
1860 $pseudonym = $session['au_pseudonym'].($count > 1 ? ' ['.$count.']':'');
1864 $title = $this->translator
->trans($session['l_title']).($count > 1 ? ' ['.$count.']':'');
1867 $calendar[$Ymd]['sessions'][$session['t_id'].sprintf('%02d', $session['l_id'])] = [
1868 'id' => $session['id'],
1869 'start' => $session['start'],
1870 'stop' => $session['stop'],
1871 'location' => $this->translator
->trans($session['l_title']),
1873 'pseudonym' => $pseudonym,
1875 'slot' => self
::GLYPHS
[$session['t_title']],
1876 'slottitle' => $this->translator
->trans($session['t_title']),
1877 'weather' => $weather,
1878 'weathertitle' => implode(' ', $weathertitle),
1879 'applications' => $applications,
1880 'rate' => $session['p_rate'],
1881 'hat' => $session['p_hat']
1887 ksort($calendar[$Ymd]['sessions']);
1895 * Find all session pending hourly weather
1897 * @return array<Session> The sessions to update
1899 public function findAllPendingHourlyWeather() {
1900 //Select all sessions starting and stopping in the next 3 days
1901 //XXX: select session starting after now and stopping before date(now)+3d as accuweather only provide hourly data for the next 3 days (INTERVAL 3 DAY)
1903 SELECT s.id, s.slot_id, s.location_id, s.date, s.begin, s.length, s.rainfall, s.rainrisk, s.realfeel, s.realfeelmin, s.realfeelmax, s.temperature, s.temperaturemin, s.temperaturemax, l.zipcode
1904 FROM RapsysAirBundle:Session AS s
1905 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
1906 WHERE ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) >= NOW() AND ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) < DATE(ADDDATE(NOW(), INTERVAL :accuhourly DAY))
1909 //Replace bundle entity name by table name
1910 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
1912 //Get result set mapping instance
1913 $rsm = new ResultSetMapping();
1915 //Declare all fields
1917 ->addEntityResult('RapsysAirBundle:Session', 's')
1918 ->addFieldResult('s', 'id', 'id')
1919 ->addFieldResult('s', 'date', 'date')
1920 ->addFieldResult('s', 'begin', 'begin')
1921 ->addFieldResult('s', 'length', 'length')
1922 ->addFieldResult('s', 'rainfall', 'rainfall')
1923 ->addFieldResult('s', 'rainrisk', 'rainrisk')
1924 ->addFieldResult('s', 'realfeel', 'realfeel')
1925 ->addFieldResult('s', 'realfeelmin', 'realfeelmin')
1926 ->addFieldResult('s', 'realfeelmax', 'realfeelmax')
1927 ->addFieldResult('s', 'temperature', 'temperature')
1928 ->addFieldResult('s', 'temperaturemin', 'temperaturemin')
1929 ->addFieldResult('s', 'temperaturemax', 'temperaturemax')
1930 ->addJoinedEntityResult('RapsysAirBundle:Slot', 'o', 's', 'slot')
1931 ->addFieldResult('o', 'slot_id', 'id')
1932 ->addJoinedEntityResult('RapsysAirBundle:Location', 'l', 's', 'location')
1933 ->addFieldResult('l', 'location_id', 'id')
1934 ->addFieldResult('l', 'zipcode', 'zipcode')
1935 ->addIndexBy('s', 'id');
1939 ->createNativeQuery($req, $rsm)
1944 * Find all session pending daily weather
1946 * @return array<Session> The sessions to update
1948 public function findAllPendingDailyWeather() {
1949 //Select all sessions stopping after next 3 days
1950 //XXX: select session stopping after or equal date(now)+3d as accuweather only provide hourly data for the next 3 days (INTERVAL 3 DAY)
1952 SELECT s.id, s.slot_id, s.location_id, s.date, s.begin, s.length, s.rainfall, s.rainrisk, s.realfeel, s.realfeelmin, s.realfeelmax, s.temperature, s.temperaturemin, s.temperaturemax, l.zipcode
1953 FROM RapsysAirBundle:Session AS s
1954 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
1955 WHERE ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) >= DATE(ADDDATE(NOW(), INTERVAL :accuhourly DAY)) AND ADDDATE(ADDTIME(ADDTIME(s.date, s.begin), s.length), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY) < DATE(ADDDATE(NOW(), INTERVAL :accudaily DAY))
1958 //Replace bundle entity name by table name
1959 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
1961 //Get result set mapping instance
1962 $rsm = new ResultSetMapping();
1964 //Declare all fields
1966 ->addEntityResult('RapsysAirBundle:Session', 's')
1967 ->addFieldResult('s', 'id', 'id')
1968 ->addFieldResult('s', 'date', 'date')
1969 ->addFieldResult('s', 'begin', 'begin')
1970 ->addFieldResult('s', 'length', 'length')
1971 ->addFieldResult('s', 'rainfall', 'rainfall')
1972 ->addFieldResult('s', 'rainrisk', 'rainrisk')
1973 ->addFieldResult('s', 'realfeel', 'realfeel')
1974 ->addFieldResult('s', 'realfeelmin', 'realfeelmin')
1975 ->addFieldResult('s', 'realfeelmax', 'realfeelmax')
1976 ->addFieldResult('s', 'temperature', 'temperature')
1977 ->addFieldResult('s', 'temperaturemin', 'temperaturemin')
1978 ->addFieldResult('s', 'temperaturemax', 'temperaturemax')
1979 ->addJoinedEntityResult('RapsysAirBundle:Slot', 'o', 's', 'slot')
1980 ->addFieldResult('o', 'slot_id', 'id')
1981 ->addJoinedEntityResult('RapsysAirBundle:Location', 'l', 's', 'location')
1982 ->addFieldResult('l', 'location_id', 'id')
1983 ->addFieldResult('l', 'zipcode', 'zipcode')
1984 ->addIndexBy('s', 'id');
1988 ->createNativeQuery($req, $rsm)
1993 * Find every session pending application
1995 * @return array<Session> The sessions to update
1997 public function findAllPendingApplication() {
1998 //Select all sessions not locked without application or canceled application within attribution period
1999 //XXX: DIFF(start, now) <= IF(DIFF(start, created) <= SENIOR_DELAY in DAY, DIFF(start, created) * 3 / 4, SENIOR_DELAY)
2000 //TODO: remonter les donnΓ©es pour le mail ?
2003 FROM RapsysAirBundle:Session as s
2004 LEFT JOIN RapsysAirBundle:Application AS a ON (a.id = s.application_id AND a.canceled IS NULL)
2005 JOIN RapsysAirBundle:Application AS a2 ON (a2.session_id = s.id AND a2.canceled IS NULL)
2006 WHERE s.locked IS NULL AND a.id IS NULL AND
2007 TIME_TO_SEC(TIMEDIFF(@dt_start := ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY), NOW())) <= IF(
2008 TIME_TO_SEC(@td_sc := TIMEDIFF(@dt_start, s.created)) <= :seniordelay,
2009 ROUND(TIME_TO_SEC(@td_sc) * :regulardelay / :seniordelay),
2013 ORDER BY @dt_start ASC, s.created ASC
2016 //Replace bundle entity name by table name
2017 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
2019 //Get result set mapping instance
2020 $rsm = new ResultSetMapping();
2022 //Declare all fields
2024 ->addEntityResult('RapsysAirBundle:Session', 's')
2025 ->addFieldResult('s', 'id', 'id')
2026 ->addIndexBy('s', 'id');
2030 ->createNativeQuery($req, $rsm)
2035 * Fetch session best application by session id
2037 * @param int $id The session id
2038 * @return Application|null The application or null
2040 public function findBestApplicationById($id) {
2042 * Query session applications ranked by location score, global score, created and user_id
2044 * @xxx guest (or less) with application on location within 30 day are only considered within guestdelay
2046 * @xxx regular (or less) premium application on hotspot are only considered within regulardelay
2048 * @xxx senior (or less) with 5 or less session on location are only considered within seniordelay
2050 * @xxx senior (or less) with l_pn_ratio >= 1 are only considered within seniordelay
2052 * @xxx senior (or less) with l_tr_ratio >= (o_tr_ratio + 5) are only considered within seniordelay
2054 * @xxx only consider session within one year (may be unaccurate by the day with after session)
2056 * @xxx rainfall may not be accessible for previous session and other session at d-4 (only at d-2)
2058 * @todo ??? feedback the data to inform the rejected users ???
2061 SELECT e.id, e.l_score AS score
2073 MAX(ug.group_id) AS group_id,
2088 AVG(IF(a4.id IS NOT NULL AND s4.temperature IS NOT NULL AND s4.rainfall IS NOT NULL, s4.temperature/(1+s4.rainfall), NULL)) AS o_tr_ratio,
2105 SUM(IF(a3.id IS NOT NULL, 1/ABS(DATEDIFF(ADDDATE(b.date, INTERVAL IF(b.slot_id = :afterid, 1, 0) DAY), ADDDATE(s3.date, INTERVAL IF(s3.slot_id = :afterid, 1, 0) DAY))), 0)) AS g_score,
2118 COUNT(a2.id) AS l_count,
2119 SUM(IF(a2.id IS NOT NULL, 1/ABS(DATEDIFF(ADDDATE(s.date, INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY), ADDDATE(s2.date, INTERVAL IF(s2.slot_id = :afterid, 1, 0) DAY))), 0)) AS l_score,
2120 AVG(IF(a2.id IS NOT NULL AND s2.temperature IS NOT NULL AND s2.rainfall IS NOT NULL, s2.temperature/(1+s2.rainfall), NULL)) AS l_tr_ratio,
2121 (SUM(IF(a2.id IS NOT NULL AND s2.premium = 1, 1, 0))+1)/(SUM(IF(a2.id IS NOT NULL AND s2.premium = 0, 1, 0))+1) AS l_pn_ratio,
2122 MIN(IF(a2.id IS NOT NULL, DATEDIFF(ADDDATE(s.date, INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY), ADDDATE(s2.date, INTERVAL IF(s2.slot_id = :afterid, 1, 0) DAY)), NULL)) AS l_previous,
2123 TIME_TO_SEC(TIMEDIFF(ADDDATE(ADDTIME(s.date, s.begin), INTERVAL IF(s.slot_id = :afterid, 1, 0) DAY), NOW())) AS remaining,
2127 FROM RapsysAirBundle:Session AS s
2128 JOIN RapsysAirBundle:Location AS l ON (l.id = s.location_id)
2129 JOIN RapsysAirBundle:Application AS a ON (a.session_id = s.id AND a.canceled IS NULL)
2130 LEFT JOIN RapsysAirBundle:Session AS s2 ON (s2.id != s.id AND s2.location_id = s.location_id AND s2.slot_id IN (:afternoonid, :eveningid) AND s2.application_id IS NOT NULL AND s2.locked IS NULL AND s2.date > s.date - INTERVAL 1 YEAR)
2131 LEFT JOIN RapsysAirBundle:Application AS a2 ON (a2.id = s2.application_id AND a2.user_id = a.user_id AND (a2.canceled IS NULL OR TIMESTAMPDIFF(DAY, a2.canceled, ADDDATE(ADDTIME(s2.date, s2.begin), INTERVAL IF(s2.slot_id = :afterid, 1, 0) DAY)) < 1))
2137 LEFT JOIN RapsysAirBundle:Session AS s3 ON (s3.id != b.session_id AND s3.application_id IS NOT NULL AND s3.locked IS NULL AND s3.date > b.date - INTERVAL 1 YEAR)
2138 LEFT JOIN RapsysAirBundle:Application AS a3 ON (a3.id = s3.application_id AND a3.user_id = b.user_id AND (a3.canceled IS NULL OR TIMESTAMPDIFF(DAY, a3.canceled, ADDDATE(ADDTIME(s3.date, s3.begin), INTERVAL IF(s3.slot_id = :afterid, 1, 0) DAY)) < 1))
2143 LEFT JOIN RapsysAirBundle:Session AS s4 ON (s4.id != c.session_id AND s4.location_id = c.location_id AND s4.application_id IS NOT NULL AND s4.locked IS NULL AND s4.date > c.date - INTERVAL 1 YEAR)
2144 LEFT JOIN RapsysAirBundle:Application AS a4 ON (a4.id = s4.application_id AND a4.user_id != c.user_id AND (a4.canceled IS NULL OR TIMESTAMPDIFF(DAY, a4.canceled, ADDDATE(ADDTIME(s4.date, s4.begin), INTERVAL IF(s4.slot_id = :afterid, 1, 0) DAY)) < 1))
2149 LEFT JOIN RapsysAirBundle:UserGroup AS ug ON (ug.user_id = d.user_id)
2154 IF(e.group_id <= :guestid AND e.l_previous <= :guestwait, e.remaining <= :guestdelay, 1) AND
2155 IF(e.group_id <= :regularid AND e.premium = 1 AND e.hotspot = 1, e.remaining <= :regulardelay, 1) AND
2156 IF(e.group_id <= :seniorid AND e.l_count <= :scount, e.remaining <= :regulardelay, 1) AND
2157 IF(e.group_id <= :seniorid AND e.l_pn_ratio >= :pnratio, e.remaining <= :regulardelay, 1) AND
2158 IF(e.group_id <= :seniorid AND e.l_tr_ratio >= (e.o_tr_ratio + :trdiff), e.remaining <= :regulardelay, 1)
2159 ORDER BY e.l_score ASC, e.g_score ASC, e.created ASC, e.user_id ASC
2162 //Replace bundle entity name by table name
2163 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
2165 //Set update request
2166 $upreq = 'UPDATE RapsysAirBundle:Application SET score = :score, updated = NOW() WHERE id = :id';
2168 //Replace bundle entity name by table name
2169 $upreq = str_replace($this->tableKeys
, $this->tableValues
, $upreq);
2171 //Get result set mapping instance
2172 $rsm = new ResultSetMapping();
2174 //Declare all fields
2176 ->addEntityResult('RapsysAirBundle:Application', 'a')
2177 ->addFieldResult('a', 'id', 'id')
2178 ->addFieldResult('a', 'score', 'score')
2179 ->addIndexBy('a', 'id');
2182 //XXX: setting limit in subqueries is required to prevent mariadb optimisation
2183 $applications = $this->_em
2184 ->createNativeQuery($req, $rsm)
2185 ->setParameter('sid', $id)
2192 foreach($applications as $application) {
2193 //Check if we already saved best candidate
2194 if ($ret === null) {
2195 //Return first application
2196 $ret = $application;
2199 //Update application updated field
2200 //XXX: updated field is not modified for user with bad behaviour as application is not retrieved until delay is reached
2201 $this->_em
->getConnection()->executeUpdate($upreq, ['id' => $application->getId(), 'score' => $application->getScore()], ['id' => Types
::INTEGER, 'score' => Types
::FLOAT]);
2204 //Return best ranked application
2210 * Rekey sessions and applications by chronological session id
2212 * @return bool The rekey success or failure
2214 function rekey(): bool {
2216 $cnx = $this->_em
->getConnection();
2229 GROUP_CONCAT(sa.id ORDER BY sa.id SEPARATOR "\\n") AS sa_id
2230 FROM RapsysAirBundle:Session AS s
2231 LEFT JOIN RapsysAirBundle:Application AS sa ON (sa.session_id = s.id)
2235 ORDER BY ADDDATE(ADDTIME(a.date, a.begin), INTERVAL IF(a.slot_id = :afterid, 1, 0) DAY) ASC
2238 //Replace bundle entity name by table name
2239 $req = str_replace($this->tableKeys
, $this->tableValues
, $req);
2241 //Get result set mapping instance
2242 //XXX: DEBUG: see ../blog.orig/src/Rapsys/BlogBundle/Repository/ArticleRepository.php
2243 $rsm = new ResultSetMapping();
2245 //Declare all fields
2246 //XXX: see vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Types.php
2247 //addScalarResult($sqlColName, $resColName, $type = 'string');
2248 $rsm->addScalarResult('id', 'id', 'integer')
2249 ->addScalarResult('sa_id', 'sa_id', 'string');
2250 #->addIndexByScalar('id');
2253 $rnq = $this->_em
->createNativeQuery($req, $rsm);
2256 $res = $rnq->getResult();
2259 $cnx->beginTransaction();
2261 //Set update session request
2263 UPDATE RapsysAirBundle:Session
2264 SET id = :nid, updated = NOW()
2268 //Replace bundle entity name by table name
2269 $sreq = str_replace($this->tableKeys
, $this->tableValues
, $sreq);
2271 //Set update application request
2273 UPDATE RapsysAirBundle:Application
2274 SET session_id = :nid, updated = NOW()
2275 WHERE session_id = :id
2278 //Replace bundle entity name by table name
2279 $areq = str_replace($this->tableKeys
, $this->tableValues
, $areq);
2282 $max = max(array_keys($res));
2285 //Prepare session to update
2286 foreach($res as $id => $data) {
2288 $res[$id]['t_id'] = $max +
$id +
1;
2291 $res[$id]['n_id'] = $id +
1;
2293 //Explode application ids
2294 $res[$id]['sa_id'] = explode("\n", $data['sa_id']);
2297 if ($res[$id]['n_id'] == $res[$id]['id']) {
2298 //Remove unchanged session
2305 //Disable foreign key checks
2306 $cnx->prepare('SET foreign_key_checks = 0')->execute();
2309 foreach($res as $id => $data) {
2310 //Run session update
2311 $cnx->executeUpdate($sreq, ['nid' => $res[$id]['t_id'], 'id' => $res[$id]['id']]);
2313 //Run applications update
2314 $cnx->executeUpdate($areq, ['nid' => $res[$id]['t_id'], 'id' => $res[$id]['id']]);
2318 foreach($res as $id => $data) {
2319 //Run session update
2320 $cnx->executeUpdate($sreq, ['nid' => $res[$id]['n_id'], 'id' => $res[$id]['t_id']]);
2322 //Run applications update
2323 $cnx->executeUpdate($areq, ['nid' => $res[$id]['n_id'], 'id' => $res[$id]['t_id']]);
2326 //Restore foreign key checks
2327 $cnx->prepare('SET foreign_key_checks = 1')->execute();
2329 //Commit transaction
2332 //Set update auto_increment request
2334 ALTER TABLE RapsysAirBundle:Session
2338 //Replace bundle entity name by table name
2339 $ireq = str_replace($this->tableKeys
, $this->tableValues
, $ireq);
2341 //Reset auto_increment
2345 //Rollback transaction
2348 } catch(\Exception
$e) {
2349 //Rollback transaction