Distribution.php 37.3 KB
Newer Older
1
2
3
<?php
/**
 * @file
4
 * Contains \Drupal\query_example\Controller\QueryExampleController
5
6
7
8
9
10
11
12
13
14
15
 */

namespace Drupal\epal\Controller;

use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\Core\Controller\ControllerBase;

use Symfony\Component\HttpFoundation\RedirectResponse;
16
use Drupal\Core\Database\Database;
17
18
19
20
21
use Drupal\Core\Database\Connection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;

22
23
24
25
26
//use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\TypedData\Plugin\DataType\TimeStamp;

use Drupal\Core\Language\LanguageManagerInterface;

27
28
29
30
31
32
33
34
class Distribution extends ControllerBase
{

    const SUCCESS = 0;
    const ERROR_DB = -1;
    const NO_CLASS_LIMIT_DOWN = -2;
    const SMALL_CLASS = 1;
    const NON_SMALL_CLASS = 2;
35
36
    const IS_FIRST_PERIOD = false;
    const IS_SECOND_PERIOD = true;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

    protected $entity_query;
    protected $entityTypeManager;
    protected $logger;
    protected $connection;
    protected $language;
    protected $currentuser;

    protected $pendingStudents = array();
    protected $choice_id = 1;
    protected $globalCounterId = 1;

    public function __construct(
        EntityTypeManagerInterface $entityTypeManager,
        QueryFactory $entity_query,
        Connection $connection,
        LoggerChannelFactoryInterface $loggerChannel
    ) {
		$this->entityTypeManager = $entityTypeManager;
		$this->entity_query = $entity_query;
		$connection = Database::getConnection();
		$this->connection = $connection;
		$language =  \Drupal::languageManager()->getCurrentLanguage()->getId();
		$this->language = $language;
		$currentuser = \Drupal::currentUser()->id();
		$this->currentuser = $currentuser;
		$this->logger = $loggerChannel->get('epal');
64
65
    }

66
    public static function create(ContainerInterface $container)
67
68
69
70
71
72
    {
        return new static(
          $container->get('entity_type.manager'),
          $container->get('entity.query'),
          $container->get('database'),
          $container->get('logger.factory')
73
        );
74
75
    }

76
77
    public function createDistribution(Request $request)
    {
78
        set_time_limit(600); // dev
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
        $numDistributions = 3;
        $sizeOfBlock = 1000000;

        // POST method is checked
        if (!$request->isMethod('POST')) {
            return $this->respondWithStatus([
                "message" => t("Method Not Allowed")
            ], Response::HTTP_METHOD_NOT_ALLOWED);
        }

        // user validation
        $authToken = $request->headers->get('PHP_AUTH_USER');
        $users = $this->entityTypeManager->getStorage('user')->loadByProperties(array('name' => $authToken));
        $user = reset($users);
        if (!$user) {
            return $this->respondWithStatus([
                'message' => t("User not found"),
            ], Response::HTTP_FORBIDDEN);
        }

        // user role validation
        if (false === in_array('ministry', $user->getRoles())) {
            return $this->respondWithStatus([
                'message' => t("User Invalid Role"),
            ], Response::HTTP_FORBIDDEN);
        }

        // check where distribution can be done now ($capacityDisabled / $directorViewDisabled settings)
        $config_storage = $this->entityTypeManager->getStorage('epal_config');
        $epalConfigs = $config_storage->loadByProperties(array('id' => 1));
        $epalConfig = reset($epalConfigs);
        if (!$epalConfig) {
             return $this->respondWithStatus([
                'message' => t("EpalConfig Enity not found"),
             ], Response::HTTP_FORBIDDEN);
        } else {
            $capacityDisabled = $epalConfig->lock_school_capacity->getString();
            $directorViewDisabled = $epalConfig->lock_school_students_view->getString();
            $applicantsResultsDisabled = $epalConfig->lock_results->getString();
            if ($capacityDisabled !== "1" || $directorViewDisabled !== "1" || $applicantsResultsDisabled !== "1") {
                return $this->respondWithStatus([
                    'message' => t("capacityDisabled and / or directorViewDisabled settings are false"),
                ], Response::HTTP_FORBIDDEN);
            }
        }

        $transaction = $this->connection->startTransaction();

        try {
            // initialize/empty epal_student_class if there are already data in it!
            if ($this->initializeResults() === self::ERROR_DB) {
	            $transaction->rollback();
                return $this->respondWithStatus([
132
                    "message" => t("Unexpected Error")
133
134
135
136
137
                ], Response::HTTP_INTERNAL_SERVER_ERROR);
            }
            if (($limitUp_class = $this->retrieveCapacityLimitUp("1")) === self::ERROR_DB) {
	            $transaction->rollback();
                return $this->respondWithStatus([
138
                    "message" => t("Unexpected Error")
139
140
141
142
143
144
                ], Response::HTTP_INTERNAL_SERVER_ERROR);
            }

            while ($this->choice_id <= $numDistributions) {

                if ($this->choice_id === 1) {
145
					// υπολογισμός πλήθους για να καθοριστεί ο αριθμός των fetches που θα κάνουμε με συγκεκριμένο sizeOfBlock
146
147
					$sCon = $this->connection->select('epal_student', 'eStudent')
						->fields('eStudent', array('id'));
148
                    $sCon->join('epal_student_epal_chosen', 'epals', "eStudent.id = epals.student_id AND epals.choice_no = {$this->choice_id}");
149
					$numData = $sCon->countQuery()->execute()->fetchField();
150

151
152
153
	                $num = 1;
					$j = 1;
                    while ($num <= $numData) {
154
                        if (($total_located = $this->locateStudent($this->choice_id, self::IS_FIRST_PERIOD, 1 + $sizeOfBlock * ($j-1), $j * $sizeOfBlock, null)) === self::ERROR_DB) {
155
156
				            $transaction->rollback();
                            return $this->respondWithStatus([
157
                                "message" => t("Unexpected Error")
158
159
                            ], Response::HTTP_INTERNAL_SERVER_ERROR);
                        }
160
161
                        $num += $total_located;
                        $j += 1;
162
163
164
165
                    }
                } 
				else { // $this->choice_id !== 1
                    if (sizeof($this->pendingStudents) != 0) {
166
                        if ($this->locateStudent($this->choice_id, self::IS_FIRST_PERIOD, null, null, $this->pendingStudents) === self::ERROR_DB) {
167
168
				            $transaction->rollback();
                            return $this->respondWithStatus([
169
                                "message" => t("Unexpected Error")
170
                            ], Response::HTTP_INTERNAL_SERVER_ERROR);
171
                        
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
                        }
                    } else { // αν δεν υπάρχουν εκκρεμότητες, μην συνεχίζεις με άλλο πέρασμα
                        break;
                    }
                }

                // Για κάθε σχολείο βρες τα τμήματα
                // Για κάθε τμήμα βρες αν χωράνε και διευθέτησε (checkCapacityAndArrange)
                // checkCapacityAndArrange (school_id, class_id, sectorORcourse_id, limitUp, schoolCapacity)

                $sCon = $this->connection->select('eepal_school_field_data', 'eSchool')
                    ->fields('eSchool', array('id', 'capacity_class_a', 'operation_shift'));
                $eepalSchools = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);

                foreach ($eepalSchools as $eepalSchool) {
                    if ($this->checkCapacityAndArrange($eepalSchool->id, "1", "-1", $limitUp_class, $eepalSchool->capacity_class_a) === self::ERROR_DB) {
						$transaction->rollback();
                        return $this->respondWithStatus([
190
                            "message" => t("Unexpected Error")
191
192
193
194
195
196
197
198
199
200
201
                        ], Response::HTTP_INTERNAL_SERVER_ERROR);
                    }

                    $sCon = $this->connection->select('eepal_sectors_in_epal_field_data', 'eSchool')
                        ->fields('eSchool', array('epal_id', 'sector_id', 'capacity_class_sector'))
                        ->condition('eSchool.epal_id', $eepalSchool->id, '=');
                    $eepalSectorsInEpal = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);
                    foreach ($eepalSectorsInEpal as $eepalSecInEp) {
                        if ($this->checkCapacityAndArrange($eepalSchool->id, "2", $eepalSecInEp->sector_id, $limitUp_class, $eepalSecInEp->capacity_class_sector) === self::ERROR_DB) {
				            $transaction->rollback();
                            return $this->respondWithStatus([
202
                                "message" => t("Unexpected Error")
203
204
205
206
207
                            ], Response::HTTP_INTERNAL_SERVER_ERROR);
                        }
                    }

                    $sCon = $this->connection->select('eepal_specialties_in_epal_field_data', 'eSchool')
208
                        ->fields('eSchool', array('epal_id', 'specialty_id', 'capacity_class_specialty', 'capacity_class_specialty_d'))
209
210
211
212
213
214
215
                        ->condition('eSchool.epal_id', $eepalSchool->id, '=');
                    $eepalSpecialtiesInEpal = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);
                    foreach ($eepalSpecialtiesInEpal as $eepalSpecialInEp) {
                        //Γ' Λυκείου
                        if ($this->checkCapacityAndArrange($eepalSchool->id, "3", $eepalSpecialInEp->specialty_id, $limitUp_class, $eepalSpecialInEp->capacity_class_specialty) === self::ERROR_DB) {
				            $transaction->rollback();
                            return $this->respondWithStatus([
216
                                "message" => t("Unexpected Error")
217
218
219
220
                            ], Response::HTTP_INTERNAL_SERVER_ERROR);
                        }
                        //Δ' Λυκείου
                        if ($eepalSchool->operation_shift === "ΕΣΠΕΡΙΝΟ") {
221
                            if ($this->checkCapacityAndArrange($eepalSchool->id, "4", $eepalSpecialInEp->specialty_id, $limitUp_class, $eepalSpecialInEp->capacity_class_specialty_d) === self::ERROR_DB) {
222
223
					            $transaction->rollback();
                                return $this->respondWithStatus([
224
                                    "message" => t("Unexpected Error")
225
226
227
228
229
230
231
232
233
234
235
                                ], Response::HTTP_INTERNAL_SERVER_ERROR);
                            }
                        }
                    }
                } //end for each school/department

                $this->choice_id++;
            } //end while

            if ($this->findSmallClasses() === self::ERROR_DB) {
				$transaction->rollback();
236
				return $this->respondWithStatus([
237
                    "message" => t("Unexpected Error")
238
239
240
241
242
243
244
245
246
247
				], Response::HTTP_INTERNAL_SERVER_ERROR);
				// αν αποτύχει, δεν γίνεται rollback. --> Λύση: διαγραφή των όποιων αποτελεσμάτων
                // if ($this->initializeResults() === self::ERROR_DB) {
                //         return $this->respondWithStatus([
                //                 "message" => t("Unexpected Error in initializeResults function AFTER findSmallClasses call Function")
                //             ], Response::HTTP_INTERNAL_SERVER_ERROR);
                // }
            }
        } 
        catch (\Exception $e) {
248
            $this->logger->error($e->getMessage());
249
250
            $transaction->rollback();
            return $this->respondWithStatus([
251
                "message" => t("Unexpected Error")
252
253
			], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
254

255
		return $this->respondWithStatus([
256
			'message' => "Distribution completed successfully",
257
258
		], Response::HTTP_OK);
    }
259

260

261
    /**
262
263
264
265
266
     * @param int $choice_id the order of choice 
     * @param boolean $period self::IS_FIRST_PERIOD or self::IS_SECOND_PERIOD
     * @param int $id_range_start id range start 
     * @param int $id_range_end id range end 
     * @param int[] $id_list restrict selection to specific ids 
267
268
     * @return int  Return self::ERROR_DB in case of error, else return number of 'locatedStudents'
     */
269
    public function locateStudent($choice_id, $period = false, $id_range_start = null, $id_range_end = null, $id_list = null)
270
    {
271
        $total_count = 0;        
272
        try {
273
274
            $sCon = $this->connection->select('epal_student', 'eStudent')
                ->fields('eStudent', array('id', 'currentclass', 'currentepal', 'second_period'));
275
276
277
            if ($period === self::IS_SECOND_PERIOD) {
                $sCon->condition('eStudent.second_period', 1, '=');
            }
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
            if ($id_range_start !== null) {
                $sCon->condition('eStudent.id', $id_range_start, '>=');
            }
            if ($id_range_end !== null) {
                $sCon->condition('eStudent.id', $id_range_end, '<=');
            }
            if (is_array($id_list) && count($id_list) > 0) {
                $sCon->condition('eStudent.id', $id_list, 'IN');
            }
            $sCon->join('epal_student_epal_chosen', 'epals', "eStudent.id = epals.student_id AND epals.choice_no = {$choice_id}");
            $sCon->fields('epals', array('epal_id', 'choice_no'));
            $sCon->leftJoin('epal_student_sector_field', 'sectors', "sectors.student_id = eStudent.id");
            $fieldname1 = $sCon->addField('sectors', 'sectorfield_id');
            $sCon->leftJoin('epal_student_course_field', 'courses', "courses.student_id = eStudent.id");
            $fieldname2 = $sCon->addField('courses', 'coursefield_id');
            // $sCon->addExpression('-1', 'specialization_id'); // no need, will have to check anyway

            // $epalStudents = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);
            // if ($epalStudents === false) {
            //     throw new \Exception("Cannot fetch data");
            // }

            $results = $sCon->execute();
            while ($epalStudent = $results->fetchObject()) {
                $total_count++;
303

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
                if ($epalStudent->currentclass === "2") {
                    $specialization_id = $epalStudent->sectorfield_id;
                } else if ($epalStudent->currentclass === "3" || $epalStudent->currentclass === "4") {
                    $specialization_id = $epalStudent->coursefield_id;
                } else {
                    $specialization_id = -1;
                }
                $epal_dist_id = $epalStudent->epal_id;
                $timestamp = strtotime(date("Y-m-d"));

                $this->connection->insert('epal_student_class')->fields([
                    'id' => $this->globalCounterId++,
                    'uuid' => \Drupal::service('uuid')->generate(),
                    'langcode' => $this->language,
                    'user_id' => $this->currentuser,
                    'student_id'=> $epalStudent->id,
                    'epal_id'=> $epal_dist_id,
                    'currentclass' => $epalStudent->currentclass,
                    'currentepal' => $epalStudent->currentepal,
                    'specialization_id' => $specialization_id,
                    // 'points' => $epalStudent->points,
                    'distribution_id' => $choice_id,
                    'finalized' => 1,
                    'second_period' => $epalStudent->second_period,
                    'status' => 1,
                    'created' => $timestamp,
                    'changed' => $timestamp
                ])->execute();
            } 
333
        } catch (\Exception $e) {
334
            $this->logger->error($e->getMessage());
335
336
337
            return self::ERROR_DB;
        }

338
339
        // return self::SUCCESS;
        return $total_count;
340
    }
341

342

343
344
345
346
    public function retrieveCapacityLimitUp($className)
    {
        try {
            $clCon = $this->connection->select('epal_class_limits', 'classLimits')
347
348
                ->fields('classLimits', array('limit_up'))
                ->condition('classLimits.name', $className, '=');
349
350
351
            $results = $clCon->execute()->fetchAll(\PDO::FETCH_OBJ);
            $row = reset($results);
        } catch (\Exception $e) {
352
            $this->logger->error($e->getMessage());
353
354
355
            return self::ERROR_DB;
        }

356
        return $row->limit_up;
357
    }
358

359

360
361
    public function checkCapacityAndArrange($epalId, $classId, $secCourId, $limitup, $capacity)
    {
362

363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
        if (!isset($capacity)) {
            $capacity = 0;
        }

        try {
            $clCon = $this->connection->select('epal_student_class', 'studentClass')
                ->fields('studentClass', array('epal_id', 'student_id', 'points', 'currentepal', 'currentclass', 'specialization_id'))
                ->condition('studentClass.epal_id', $epalId, '=')
                ->condition('studentClass.currentclass', $classId, '=')
                ->condition('studentClass.specialization_id', $secCourId, '=');
            $epalStudentClass = $clCon->execute()->fetchAll(\PDO::FETCH_OBJ);

            $limit = $limitup * $capacity;
            if (sizeof($epalStudentClass) > $limit) {
                $this->makeSelectionOfStudents($epalStudentClass, $limit);
378
379
380
381
382
383
            } elseif ($this->choice_id !== 1) {
                // αφαίρεσε όσους μαθητές βρίσκονται στον πίνακα εκκρεμοτήτων
                $clear_ids = array_map(function ($epalStudCl) {
                    return $epalStudCl->student_id;
                }, $epalStudentClass);
                $this->removeFromPendingStudents($clear_ids);
384
            }
385
386
        } catch (\Exception $e) {
            $this->logger->error($e->getMessage());
387
388
389
390
391
            return self::ERROR_DB;
        }

        return self::SUCCESS;
    }
392

393

394
395
    public function removeFromPendingStudents($val)
    {
396
397
398
        if (is_array($val) && count($val) > 0) {
            $this->pendingStudents = array_diff($this->pendingStudents, $val);
        } elseif (($key = array_search($val, $this->pendingStudents)) !== false) {
399
400
401
            unset($this->pendingStudents[$key]);
        }
    }
402

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    public function makeSelectionOfStudents_VERSION_WITH_POINTS(&$students, $limit)
    {
        //συνάρτηση επιλογής μαθητών σε περίπτωση υπερχείλισης
        // (1) έχουν απόλυτη προτεραιότητα όσοι ήδη φοιτούσαν στο σχολείο (σε περίπτωση που φοιτούσαν περισσότεροι από την χωρητικότητα, τους δεχόμαστε όλους)
        // (2) αν απομένουν κενές θέσεις, επέλεξε από τους εναπομείναντες μαθητές αυτούς με τα περισσότερα μόρια. Σε περίπτωση ισοβαθμίας δεχόμαστε όλους όσους ισοβαθμούν.

        foreach ($students as $student) {
            $student->student_id;
            //print_r("<br>STUDENT_ID:" . $student->student_id);
        }

        //εύρεση αριθμού μαθητών που ήδη φοιτούσαν στο σχολείο
        $cnt = 0;
        foreach ($students as $student) {
            if ($student->currentepal === $student->epal_id) {
                $cnt++;
                if ($this->choice_id !== 1) {
                    ////διέγραψε τον μαθητή από τον πίνακα εκκρεμοτήτων (αν βρίσκεται εκεί)
                    $this->removeFromPendingStudents($student->student_id);
                }
            }
        }
        //print_r("<br>#ΕΓΓΡΑΦΩΝ ΠΟΥ ΟΙ ΜΑΘΗΤΕΣ ΦΟΙΤΟΥΣΑΝ ΗΔΗ:" . $cnt);

        $newlimit = $limit - $cnt;
        //print_r("<br>ΑΝΩΤΑΤΟ ΟΡΙΟ ΜΑΘΗΤΩΝ:" . $limit);
        //print_r("<br>#ΜΑΘΗΤΩΝ ΓΙΑ ΝΑ ΕΠΙΛΕΓΟΥΝ ΜΕ ΜΟΡΙΑ:" . $newlimit);

        $points_arr = [];
        foreach ($students as $student) {
            if ($student->currentepal !== $student->epal_id) {
                $points_arr[] = $student->points;
            }
        }

        rsort($points_arr);
        //for ($i=0; $i < sizeof($points_arr); $i++)
            //print_r("<br>ΜΟΡΙΑ ΜΕΤΑ ΤΗΝ ΤΑΞΙΝΟΜΙΣΗ: " . $points_arr[$i]);

        //print_r("<br>ΟΡΙΟ ΜΟΡΙΩΝ: " . $points_arr[$newlimit-1]);

        $transaction = $this->connection->startTransaction();

        foreach ($students as $student) {
            if ($student->currentepal !== $student->epal_id) {
                if ($student->points < $points_arr[$newlimit-1]) {
                    //print_r("<br>ΣΕ ΕΚΚΡΕΜΟΤΗΤΑ - ΔΙΑΓΡΑΦΗ: " . $student->student_id);
                    //βάλε τον μαθητή στον πίνακα εκκρεμοτήτων και διέγραψέ τον από τον προσωρινό πίνακα αποτελεσμάτων
                    array_push($this->pendingStudents, $student->student_id);
                    try {
                        $this->connection->delete('epal_student_class')
454
455
							->condition('student_id', $student->student_id)
							->execute();
456
                    } catch (\Exception $e) {
457
                        $this->logger->error($e->getMessage());
458
459
                        $transaction->rollback();
                        return $this->respondWithStatus([
460
461
							"message" => t("An unexpected problem occured during DELETE proccess in makeSelectionOfStudents Method of Distribution")
						], Response::HTTP_INTERNAL_SERVER_ERROR);
462
463
464
465
466
467
468
469
470
471
472
                    }
                } else {
                    if ($this->choice_id !== 1) {
                        //διέγραψε τον μαθητή από τον πίνακα εκκρεμοτήτων (αν βρίσκεται εκεί)
                        $this->removeFromPendingStudents($student->student_id);
                    }
                }
            }
        }

        return $this->respondWithStatus([
473
474
			"message" => t("makeSelectionOfStudents Method of Distribution has made successfully")
		], Response::HTTP_OK);
475
    }
476

477
478
479
480
481
482
483
484
    public function makeSelectionOfStudents(&$students, $limit)
    {
        // συνάρτηση επιλογής μαθητών σε περίπτωση υπερχείλισης
        // (1) έχουν απόλυτη προτεραιότητα όσοι ήδη φοιτούσαν στο σχολείο (σε περίπτωση που φοιτούσαν περισσότεροι από την χωρητικότητα, τους δεχόμαστε όλους)
        // (2) αν απομένουν κενές θέσεις,...τοποθέτησε και όλους τους άλλους!!.

        //αν capacity = 0, ..διέγραψέ τους από εκεί που τοποθετήθηκαν προσωρινά
        if ($limit === 0) {
485
486
487
488
489
490
491
492
493
494
            try {
                $clear_ids = array_map(function ($student) {
                    return $student->student_id;
                }, $students);
                $this->connection->delete('epal_student_class')
                    ->condition('student_id', $clear_ids, 'IN')
                    ->execute();
            } catch (\Exception $e) {
                $this->logger->error($e->getMessage());
                return self::ERROR_DB;
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
            }
            return self::SUCCESS;
        }

        // εύρεση αριθμού μαθητών που ήδη φοιτούσαν στο σχολείο
        $cnt = 0;
        foreach ($students as $student) {
            if ($student->currentepal === $student->epal_id) {
                $cnt++;
                if ($this->choice_id !== 1) {
                    // διέγραψε τον μαθητή από τον πίνακα εκκρεμοτήτων (αν βρίσκεται εκεί)
                    // Κάτι τέτοιο δεν είναι δυνατό πια! (έκδοση χωρίς μόρια..)
                    $this->removeFromPendingStudents($student->student_id);
                }
            }
        }

        $newlimit = $limit - $cnt;
        //Αν δεν απέμειναν θέσεις (δηλαδή αν η χωρητικότητα είναι μικρότερη ή ίση από το πλήθος μαθητών που ήδη φοιτούν στο σχολείο)
        //τότε διέγραψέ τους από τον προσωρινό πίνακα αποτελεσμάτων και βάλε τους στον στον πίνακα εκκρεμοτήτων
515
516
517
518
519
520
521
522
        $not_current_students = array_filter($students, function ($student) {
            return $student->currentepal !== $student->epal_id;
        });
        if (count($not_current_students) > 0) {
            try {
                $clear_ids = array_map(function ($student) {
                    return $student->student_id;
                }, $not_current_students);
523
                if ($newlimit <= 0) {
524
525
526
527
528
                    $this->pendingStudents = array_merge($this->pendingStudents, $clear_ids);
                    $this->connection->delete('epal_student_class')
                        ->condition('student_id', $clear_ids, 'IN')
                        ->execute();
                } else { // $newlimit > 0
529
                    if ($this->choice_id !== 1) {
530
                        $this->removeFromPendingStudents($clear_ids);
531
532
                    }
                }
533
534
535
536
537
            } catch (\Exception $e) {
                $this->logger->error($e->getMessage());
                return self::ERROR_DB;
            }
        }
538
539
540

        return self::SUCCESS;
    }
541

542
543
    private function initializeResults()
    {
544
		//initialize/empty epal_student_class if there are already data in it!
545
546
547
        try {
            $this->connection->delete('epal_student_class')->execute();
        } catch (\Exception $e) {
548
            $this->logger->error($e->getMessage());
549
550
            return self::ERROR_DB;
        }
551

552
553
		return self::SUCCESS;
    }
554

555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
    private function findSmallClasses()
    {
        //Για κάθε σχολείο βρες τα ολιγομελή τμήματα
        $sCon = $this->connection->select('eepal_school_field_data', 'eSchool')
            ->fields('eSchool', array('id', 'metathesis_region','operation_shift'));
        $eepalSchools = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);

        foreach ($eepalSchools as $eepalSchool) {

            // Α' τάξη
            if ($this->isSmallClass($eepalSchool->id, "1", "-1", $eepalSchool->metathesis_region) === self::SMALL_CLASS) {
                if ($this->markStudentsInSmallClass($eepalSchool->id, "1", "-1") === self::ERROR_DB) {
                    return self::ERROR_DB;
                }
            }

            // Β' τάξη
            $sCon = $this->connection->select('eepal_sectors_in_epal_field_data', 'eSchool')
                ->fields('eSchool', array('epal_id', 'sector_id'))
                ->condition('eSchool.epal_id', $eepalSchool->id, '=');
            $eepalSectorsInEpal = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);
            foreach ($eepalSectorsInEpal as $eepalSecInEp) {
                if ($this->isSmallClass($eepalSchool->id, "2", $eepalSecInEp->sector_id, $eepalSchool->metathesis_region) === self::SMALL_CLASS) {
                    if ($this->markStudentsInSmallClass($eepalSchool->id, "2", $eepalSecInEp->sector_id) === self::ERROR_DB) {
                        return self::ERROR_DB;
                    }
                }
            }

            // Γ' τάξη
            $sCon = $this->connection->select('eepal_specialties_in_epal_field_data', 'eSchool')
                ->fields('eSchool', array('epal_id', 'specialty_id'))
                ->condition('eSchool.epal_id', $eepalSchool->id, '=');
            $eepalSpecialtiesInEpal = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);
            foreach ($eepalSpecialtiesInEpal as $eepalSpecialInEp) {
                if ($this->isSmallClass($eepalSchool->id, "3", $eepalSpecialInEp->specialty_id, $eepalSchool->metathesis_region) === self::SMALL_CLASS) {
                    if ($this->markStudentsInSmallClass($eepalSchool->id, "3", $eepalSpecialInEp->specialty_id) === self::ERROR_DB) {
                        return self::ERROR_DB;
                    }
                }
            }

            // Δ' τάξη
            if ($eepalSchool->operation_shift === "ΕΣΠΕΡΙΝΟ") {
                $sCon = $this->connection->select('eepal_specialties_in_epal_field_data', 'eSchool')
                    ->fields('eSchool', array('epal_id', 'specialty_id'))
                    ->condition('eSchool.epal_id', $eepalSchool->id, '=');
                $eepalSpecialtiesInEpal = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);
                foreach ($eepalSpecialtiesInEpal as $eepalSpecialInEp) {
                    if ($this->isSmallClass($eepalSchool->id, "4", $eepalSpecialInEp->specialty_id, $eepalSchool->metathesis_region) === self::SMALL_CLASS) {
                        if ($this->markStudentsInSmallClass($eepalSchool->id, "4", $eepalSpecialInEp->specialty_id) === self::ERROR_DB) {
                            return self::ERROR_DB;
                        }
                    }
                }
610
            } //end if ΕΣΠΕΡΙΝΟ
611
612
613
614
615
616
617
618
        } //end for each school/department

        return self::SUCCESS;
    }   //end function


    private function isSmallClass($schoolId, $classId, $sectorOrcourseId, $regionId)
    {
619

620
        $limitDown = $this->retrieveLimitDown($classId, $regionId);
621

622
623
624
625
626
        if ($limitDown === self::NO_CLASS_LIMIT_DOWN) {
            return self::NO_CLASS_LIMIT_DOWN;
        } elseif ($limitDown === self::ERROR_DB) {
            return self::ERROR_DB;
        }
627

628
        $numStudents = $this->countStudents($schoolId, $classId, $sectorOrcourseId);
629

630
631
632
        if ($numStudents === self::ERROR_DB) {
            return self::ERROR_DB;
        }
633

634
        //Αν $numStudents == 0, γύρισε false, ώστε να μη γίνει περιττή κλήση στην markStudentsInSmallClass
635
636
637
638
639
        if (($numStudents < $limitDown) && ($numStudents > 0)) {
            return self::SMALL_CLASS;
        } else {
            return self::NON_SMALL_CLASS;
        }
640
641
    }

642
643
    private function retrieveLimitDown($classId, $regionId)
    {
644

645
646
647
648
649
650
651
652
653
654
655
656
657
658
        try {
            $sCon = $this->connection->select('epal_class_limits', 'eClassLimit')
                ->fields('eClassLimit', array('limit_down'))
                ->condition('eClassLimit.name', $classId, '=')
                ->condition('eClassLimit.category', $regionId, '=');
            $classLimits = $sCon->execute()->fetchAll(\PDO::FETCH_OBJ);
            if (sizeof($classLimits) === 1) {
                $classLimit = reset($classLimits);
                return $classLimit->limit_down;
            } else {
                return self::NO_CLASS_LIMIT_DOWN;
            }
        } //end try
        catch (\Exception $e) {
659
            $this->logger->error($e->getMessage());
660
661
            return self::ERROR_DB;
        }
662
    } //end function
663
664
665
666
667
668
669
670
671
672
673

    private function countStudents($schoolId, $classId, $sectorOrcourseId)
    {
        try {
            $sCon = $this->connection->select('epal_student_class', 'eStudent')
                ->fields('eStudent', array('id'))
                ->condition('eStudent.epal_id', $schoolId, '=')
                ->condition('eStudent.currentclass', $classId, '=')
                ->condition('eStudent.specialization_id', $sectorOrcourseId, '=');
            return $sCon->countQuery()->execute()->fetchField();
        } catch (\Exception $e) {
674
            $this->logger->error($e->getMessage());
675
676
677
            return self::ERROR_DB;
        }
    }
678

679
680
681
682
    private function markStudentsInSmallClass($schoolId, $classId, $sectorOrcourseId)
    {
        try {
            $query = $this->connection->update('epal_student_class');
683
            $query->fields(['finalized' => 0]);
684
685
686
687
688
689
690
            $query->condition('epal_id', $schoolId);
            $query->condition('currentclass', $classId);
            if ($sectorOrcourseId !== "-1") {
                $query->condition('specialization_id', $sectorOrcourseId);
            }
            $query->execute();
        } catch (\Exception $e) {
691
            $this->logger->error($e->getMessage());
692
693
694
695
            return self::ERROR_DB;
        }
        return self::SUCCESS;
    }
696

697
698
    public function locateSecondPeriodStudents(Request $request)
    {
699
700
        set_time_limit(600); // dev

701
702
703
704
705
706
        //POST method is checked
        if (!$request->isMethod('POST')) {
            return $this->respondWithStatus([
				"message" => t("Method Not Allowed")
			], Response::HTTP_METHOD_NOT_ALLOWED);
        }
707

708
709
710
711
712
713
714
715
716
        //user validation
        $authToken = $request->headers->get('PHP_AUTH_USER');
        $users = $this->entityTypeManager->getStorage('user')->loadByProperties(array('name' => $authToken));
        $user = reset($users);
        if (!$user) {
			return $this->respondWithStatus([
				'message' => t("User not found"),
			], Response::HTTP_FORBIDDEN);
        }
717

718
719
        //user role validation
        if (false === in_array('ministry', $user->getRoles())) {
720
			return $this->respondWithStatus([
721
722
723
				'message' => t("User Invalid Role"),
			], Response::HTTP_FORBIDDEN);
        }
724

725
726
        //check where distribution can be done now
        $secondPeriodEnabled = "0";
727

728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
        $config_storage = $this->entityTypeManager->getStorage('epal_config');
        $epalConfigs = $config_storage->loadByProperties(array('id' => 1));
        $epalConfig = reset($epalConfigs);
        if (!$epalConfig) {
             return $this->respondWithStatus([
				'message' => t("EpalConfig Enity not found"),
			], Response::HTTP_FORBIDDEN);
        } else {
             $secondPeriodEnabled = $epalConfig->activate_second_period->getString();
        }
        if ($secondPeriodEnabled === "0") {
             return $this->respondWithStatus([
				'message' => t("secondPeriodEnabled setting is false"),
			], Response::HTTP_FORBIDDEN);
        }
743

744
745
        $transaction = $this->connection->startTransaction();

746
747
        try {
            if ($this->initializeResultsInSecondPeriod() === self::ERROR_DB) {
748
				$transaction->rollback();
749
				return $this->respondWithStatus([
750
					"message" => t("Unexpected Error")
751
752
				], Response::HTTP_INTERNAL_SERVER_ERROR);
            }
753

754
            // τοποθέτηση όλων των μαθητών Β' περιόδου στην πρώτη τους προτίμηση'
755
            $this->globalCounterId = $this->retrieveLastStudentId() + 1;
756
            if ($this->locateStudent(1, self::IS_SECOND_PERIOD) === self::ERROR_DB) {
757
				$transaction->rollback();
758
				return $this->respondWithStatus([
759
					"message" => t("Unexpected Error"),
760
761
				], Response::HTTP_INTERNAL_SERVER_ERROR);
            }
762

763
            //επαναϋπολογισμός όλων των ολιγομελών τμημάτων (Προσοχή: αφορά ΟΛΟΥΣ τους μαθητές - κανονικής και Β΄ περιόδου)
764

765
766
            //αρχικοποίηση flag finalize σε 1 για όλους
            if ($this->setFinalize() === self::ERROR_DB) {
767
				$transaction->rollback();
768
				return $this->respondWithStatus([
769
					"message" => t("Unexpected Error")
770
771
				], Response::HTTP_INTERNAL_SERVER_ERROR);
            }
772

773
774
            //εύρεση ολιγομελών και καταχώρηση μαθητών σε αυτά με κατάλληλη ένδειξη (finalize=0)
            if ($this->findSmallClasses() === self::ERROR_DB) {
775
776
777
778
779
780
781
				//αν αποτύχει, δεν γίνεται rollback. --> Λύση: διαγρα΄φή των όποιων αποτελεσμάτων
                // if ($this->initializeResultsInSecondPeriod() === self::ERROR_DB) {
				// 	$transaction->rollback();
				// 	return $this->respondWithStatus([
				// 		"message" => t("Unexpected Error in initializeResults function AFTER findSmallClasses call Function")
				// 	], Response::HTTP_INTERNAL_SERVER_ERROR);
                // }
782

783
				$transaction->rollback();
784
				return $this->respondWithStatus([
785
					"message" => t("Unexpected Error")
786
787
788
				], Response::HTTP_INTERNAL_SERVER_ERROR);
            }
        } catch (\Exception $e) {
789
            $this->logger->error($e->getMessage());
790
			$transaction->rollback();
791
            return $this->respondWithStatus([
792
                "message" => t("Unexpected Error")
793
794
			], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
795

796
797
798
799
		return $this->respondWithStatus([
			'message' => "locateSecondPeriodStudents has made successfully",
		], Response::HTTP_OK);
    }
800

801
802
803
804
805
806
807
808
809
    private function setFinalize()
    {
        try {
            $query = $this->connection->update('epal_student_class');
            $query->fields([
                'finalized' => 1,
            ]);
            $query->execute();
        } catch (\Exception $e) {
810
            $this->logger->error($e->getMessage());
811
812
813
814
            return self::ERROR_DB;
        }
        return self::SUCCESS;
    }
815

816
817
    private function initializeResultsInSecondPeriod()
    {
818
		// initialize/empty epal_student_class if there are already data in it!
819
        try {
820
821
822
            $this->connection->delete('epal_student_class')
                ->condition('second_period', 1)
                ->execute();
823
        } catch (\Exception $e) {
824
            $this->logger->error($e->getMessage());
825
826
            return self::ERROR_DB;
        }
827
828

		return self::SUCCESS;
829
    }
830

831
832
    private function retrieveLastStudentId()
    {
833
834
835
836
837
838
        $sCon = $this->connection->select('epal_student', 'eStudent');
        $sCon->addExpression('max(eStudent.id)', 'max_id');
        $max_id = intval($sCon->execute()->fetchField());

        return $max_id;
    }
839

840
841
842
843
844
845

    private function respondWithStatus($arr, $s)
    {
		$res = new JsonResponse($arr);
		$res->setStatusCode($s);
		return $res;
846
    }
847

848
}