aboutsummaryrefslogtreecommitdiff
path: root/lib/models/Semester.php
diff options
context:
space:
mode:
authorPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
committerPhilipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de>2024-09-24 10:53:31 +0200
commit4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch)
tree5c07151ae61276d334e88f6309c30d439a85c12e /lib/models/Semester.php
parentda0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff)
parent97a188592c679890a25c37ab78463add76a52ff7 (diff)
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'lib/models/Semester.php')
-rw-r--r--lib/models/Semester.php515
1 files changed, 515 insertions, 0 deletions
diff --git a/lib/models/Semester.php b/lib/models/Semester.php
new file mode 100644
index 0000000..ef3246b
--- /dev/null
+++ b/lib/models/Semester.php
@@ -0,0 +1,515 @@
+<?php
+
+/**
+ * Semester.php
+ * model class for table semester_data
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @author André Noack <noack@data-quest.de>
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ *
+ * @property string $id alias column for semester_id
+ * @property string $semester_id database column
+ * @property I18NString $name database column
+ * @property I18NString $semester_token database column
+ * @property I18NString $token alias column for semester_token
+ * @property int|null $beginn database column
+ * @property int|null $ende database column
+ * @property int|null $sem_wechsel database column
+ * @property int|null $vorles_beginn database column
+ * @property int|null $vorles_ende database column
+ * @property int $visible database column
+ * @property string|null $external_id database column
+ * @property int|null $mkdate database column
+ * @property int|null $chdate database column
+ * @property-read mixed $first_sem_week additional field
+ * @property-read mixed $last_sem_week additional field
+ * @property-read mixed $current additional field
+ * @property-read mixed $past additional field
+ * @property-read mixed $short_name additional field
+ * @property mixed $absolute_seminars_count additional field
+ * @property mixed $duration_seminars_count additional field
+ * @property mixed $continuous_seminars_count additional field
+ */
+class Semester extends SimpleORMap
+{
+ /**
+ * Configures this model.
+ *
+ * @param array $config
+ */
+ protected static function configure($config = [])
+ {
+ $config['db_table'] = 'semester_data';
+
+ $config['additional_fields']['first_sem_week']['get'] = 'getFirstSemesterWeek';
+ $config['additional_fields']['last_sem_week']['get'] = 'getLastSemesterWeek';
+ $config['additional_fields']['current']['get'] = 'isCurrent';
+ $config['additional_fields']['past']['get'] = 'isPast';
+ $config['additional_fields']['short_name']['get'] = function($semester) {
+ return (string) $semester->semester_token ?: (string) $semester->name;
+ };
+
+ $config['additional_fields']['absolute_seminars_count'] = [
+ 'get' => 'seminarCounter',
+ 'set' => false,
+ ];
+ $config['additional_fields']['duration_seminars_count'] = [
+ 'get' => 'seminarCounter',
+ 'set' => false,
+ ];
+ $config['additional_fields']['continuous_seminars_count'] = [
+ 'get' => 'seminarCounter',
+ 'set' => false,
+ ];
+
+ $config['alias_fields']['token'] = 'semester_token';
+
+ $config['registered_callbacks']['after_store'][] = 'refreshCache';
+ $config['registered_callbacks']['after_delete'][] = 'refreshCache';
+
+ $config['i18n_fields']['name'] = true;
+ $config['i18n_fields']['semester_token'] = true;
+
+ parent::configure($config);
+ }
+
+ /**
+ * cache
+ */
+ private static $semester_cache;
+ private static $current_semester;
+
+
+ /**
+ * returns semester object for given id or null
+ * @param string $id
+ * @return NULL|Semester
+ */
+ public static function find($id)
+ {
+ $semester_cache = self::getAll();
+ return $semester_cache[$id] ?? null;
+ }
+
+ /**
+ * returns Semester for given timestamp
+ * @param integer $timestamp
+ * @return null|Semester
+ */
+ public static function findByTimestamp($timestamp)
+ {
+ foreach (self::getAll() as $semester) {
+ if ($timestamp >= $semester->beginn && $timestamp <= $semester->ende) {
+ return $semester;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * returns following Semester for given timestamp
+ * @param integer $timestamp
+ * @return null|Semester
+ */
+ public static function findNext($timestamp = null)
+ {
+ $timestamp = $timestamp ?: time();
+ $semester = self::findByTimestamp($timestamp);
+ if ($semester) {
+ return self::findByTimestamp((int)$semester->ende + 1);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the previous semester for a semester specified by a timestamp.
+ * If no timestamp is specified, the previous semester of the current semester is returned.
+ *
+ * @param integer|null $timestamp The timestamp of the semester whose predecessor
+ * shall be found. Defaults to null.
+ *
+ * @return null|Semester A previous semester to the specified one or null, if no such semester
+ * could be found.
+ */
+ public static function findPrevious($timestamp = null)
+ {
+ $timestamp = $timestamp ?: time();
+ $semester = self::findByTimestamp($timestamp);
+ if ($semester) {
+ return self::findByTimestamp((int)$semester->beginn - 1);
+ }
+
+ return null;
+ }
+
+ /**
+ * returns current Semester
+ */
+ public static function findCurrent()
+ {
+ self::getAll();
+ return self::$current_semester;
+ }
+
+ /**
+ * Return a specially orderd array of all semesters
+ */
+ public static function findAllVisible($with_before_first = true): array
+ {
+ return array_values(
+ array_filter(self::getAllAsArray(), function ($semester, $key) use($with_before_first) {
+ return $GLOBALS['perm']->have_perm('admin') || !empty($semester['visible']) || ((int)$key === 0 && $with_before_first);
+ }, ARRAY_FILTER_USE_BOTH)
+ );
+ }
+
+ /**
+ * returns array of all existing semester objects
+ * orderd by begin
+ * @param boolean $force_reload
+ * @return array
+ */
+ public static function getAll($force_reload = false)
+ {
+ if (!is_array(self::$semester_cache) || $force_reload) {
+ self::$semester_cache = [];
+ if (!$force_reload) {
+ $cache = \Studip\Cache\Factory::getCache();
+ $semester_data_array = unserialize($cache->read('DB_SEMESTER_DATA'));
+ if ($semester_data_array) {
+ foreach ($semester_data_array as $semester_data) {
+ $semester = self::buildExisting($semester_data);
+ self::$semester_cache[$semester->getId()] = $semester;
+ if ($semester->isCurrent()) {
+ self::$current_semester = $semester;
+ }
+ }
+ }
+ }
+ if (!count(self::$semester_cache)) {
+ $semester_data = [];
+ foreach (self::findBySql('1 ORDER BY beginn') as $semester) {
+ self::$semester_cache[$semester->getId()] = $semester;
+ if ($semester->isCurrent()) {
+ self::$current_semester = $semester;
+ }
+ $semester_data[] = $semester->toRawArray();
+ }
+ $cache = \Studip\Cache\Factory::getCache();
+ $cache->write('DB_SEMESTER_DATA', serialize($semester_data));
+ }
+ }
+ return self::$semester_cache;
+ }
+
+ /**
+ * Returns a list of all semesters as array, optionally with an entry at
+ * the beginning that represents the time before the first semester in the
+ * system.
+ *
+ * @param boolean $with_before_first Show the optional first entry as described above
+ * @param boolean $force_reload
+ * @return array
+ */
+ public static function getAllAsArray($with_before_first = true, $force_reload = false)
+ {
+ $result = array_map(function ($semester) {
+ return $semester->toArray();
+ }, self::getAll($force_reload));
+ $result = array_values($result);
+
+ if ($with_before_first) {
+ array_unshift($result, [
+ 'name' => _('abgelaufene Semester'),
+ 'past' => true,
+ ]);
+ }
+
+ return $result;
+ }
+
+ /**
+ * returns the index for a given semester id, in an array returned from self::getAllAsArray(), beware of second parameter
+ *
+ * @param $semester_id
+ * @param bool $with_before_first
+ * @param bool $only_visible
+ * @return bool|int
+ * @deprecated ASK YOURSELF WHAT THE F!!! YOU ARE DOING
+ */
+ public static function getIndexById($semester_id, $with_before_first = true, $only_visible = false)
+ {
+ if($only_visible) {
+ $semesters = self::findAllVisible($with_before_first);
+ } else {
+ $semesters = self::getAllAsArray($with_before_first);
+ }
+ foreach ($semesters as $index => $semester) {
+ if (isset($semester['semester_id']) && $semester['semester_id'] === $semester_id) {
+ return $index;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns an html fragment with a semester select-box
+ *
+ * @param array $select_attributes
+ * @param integer $default
+ * @param string $option_value
+ * @param boolean $include_all
+ * @param boolean $use_semester_id
+ * @return string
+ */
+ public static function getSemesterSelector(
+ $select_attributes = null,
+ $default = 0,
+ $option_value = 'semester_id',
+ $include_all = true,
+ $use_semester_id = true
+ )
+ {
+ $select_attributes = array_merge([
+ 'name' => 'sem_select',
+ ], $select_attributes ?? []);
+
+ $semester = Semester::findAllVisible();
+
+ unset($semester[0]);
+
+ if ($include_all) {
+ $semester['all'] = [
+ 'name' => _('alle'),
+ 'semester_id' => 0
+ ];
+ }
+ $semester = array_reverse($semester, true);
+
+ $template = $GLOBALS['template_factory']->open('shared/semester-selector');
+ $template->semesters = $semester;
+ $template->select_attributes = $select_attributes;
+ $template->default = $default;
+ $template->option_value = $option_value;
+ $template->use_semester_id = $use_semester_id;
+ return $template->render();
+ }
+
+ /**
+ * Caches seminar counts
+ */
+ protected $seminar_counts = null;
+
+ /**
+ * Counts the number of different seminar types in this semester.
+ * This method caches the result in $seminar_counts so the db
+ * will only be queried once per semester.
+ *
+ * @param String $field Name of the seminar (/additional_fields) type
+ * @return int The count of seminars of this type
+ */
+ protected function seminarCounter($field)
+ {
+ if ($this->seminar_counts === null) {
+ $query = "
+ SELECT SUM(IF(semester_courses.semester_id IS NULL, 1, 0)) AS continuous,
+ 0 AS duration,
+ SUM(IF(semester_courses.semester_id IS NOT NULL, 1, 0)) AS absolute
+ FROM seminare
+ LEFT JOIN semester_courses ON (seminare.Seminar_id = semester_courses.course_id)
+ WHERE start_time <= :beginn
+ AND (semester_courses.semester_id IS NULL OR semester_courses.semester_id = :semester_id)
+ ";
+ $statement = DBManager::get()->prepare($query);
+ $statement->bindValue(':beginn', $this['beginn']);
+ $statement->bindValue(':semester_id', $this['semester_id']);
+ $statement->execute();
+ $this->seminar_counts = $statement->fetch(PDO::FETCH_ASSOC);
+ }
+
+ $index = str_replace('_seminars_count', '', $field);
+ return (int)$this->seminar_counts[$index];
+ }
+
+ /**
+ * Returns the calendar week number of the first week of the lecture
+ * period.
+ *
+ * @return int Calendar week number of the first week of lecture
+ */
+ public function getFirstSemesterWeek()
+ {
+ return (int)strftime('%W', $this['vorles_beginn']);
+ }
+
+ /**
+ * Returns the calendar week number of the last week of the lecture
+ * period.
+ *
+ * @return int Calendar week number of the last week of lecture
+ */
+ public function getLastSemesterWeek()
+ {
+ return (int)strftime('%W', $this['vorles_ende']);
+ }
+
+ /**
+ * Return whether this semester is in the past.
+ *
+ * @return bool Indicating whether this semester is in the past
+ */
+ public function isPast()
+ {
+ return $this->ende < time();
+ }
+
+ /**
+ * Returns whether this semester is the current semester.
+ *
+ * @return bool Indicating if this is the current semester
+ */
+ public function isCurrent()
+ {
+ return time() >= $this->beginn && time() < $this->ende;
+ }
+
+ /**
+ * Returns the start week dates for this semester (and other
+ * semesters if $end_semester is given).
+ *
+ * @param Semester $end_semester end semester, default is $this
+ * @return array containing the start weeks
+ */
+ public function getStartWeeks(?Semester $end_semester = null)
+ {
+ if (!$end_semester) {
+ $end_semester = $this;
+ }
+
+ $timestamp = $this->getCorrectedLectureBegin();
+ $end_date = $end_semester->vorles_ende;
+
+ $i = 0;
+
+ $start_weeks = [];
+ while ($timestamp < $end_date) {
+ $start_weeks[$i] = sprintf(
+ _('%u. Semesterwoche (ab %s)'),
+ $i + 1,
+ strftime('%x', $timestamp));
+
+ $i += 1;
+
+ $timestamp = strtotime('+1 week', $timestamp);
+ }
+
+ return $start_weeks;
+ }
+
+ /**
+ * Returns the corrected begin of lectures which ensures that the begin
+ * is always on a monday.
+ *
+ * @return int unix timestamp of correct begin of lectures
+ */
+ public function getCorrectedLectureBegin()
+ {
+ return strtotime('this week monday', $this->vorles_beginn);
+ }
+
+
+ /**
+ * returns "Semesterwoche" for a given timestamp
+ * @param integer $timestamp
+ * @return number|boolean
+ */
+ public function getSemWeekNumber($timestamp)
+ {
+ $current_sem_week = (int)strftime('%W', $timestamp);
+ if (strftime('%Y', $timestamp) > strftime('%Y', $this->vorles_beginn)) {
+ $current_sem_week += 52;
+ }
+ if ($this->last_sem_week < $this->first_sem_week) {
+ $last_sem_week = (int)$this->last_sem_week + 52;
+ } else {
+ $last_sem_week = $this->last_sem_week;
+ }
+ if ($current_sem_week >= $this->first_sem_week && $current_sem_week <= $last_sem_week) {
+ return $current_sem_week - $this->first_sem_week + 1;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns an array representation of this semester.
+ *
+ * @param mixed $only_these_fields List of fields to extract
+ * @return array represenation
+ */
+ public function toArray($only_these_fields = null)
+ {
+ if (!isset($only_these_fields)) {
+ $fields = array_flip(array_diff($this->known_slots(), array_keys($this->relations)));
+ unset($fields['absolute_seminars_count']);
+ unset($fields['duration_seminars_count']);
+ unset($fields['continuous_seminars_count']);
+ $only_these_fields = array_flip($fields);
+ }
+ return parent::toArray($only_these_fields);
+ }
+
+ /**
+ * Flushes the cache just after storing and deleting a semester
+ */
+ public function refreshCache()
+ {
+ \Studip\Cache\Factory::getCache()->expire('DB_SEMESTER_DATA');
+ }
+
+ /*
+ * for Admin
+ */
+ public static function getSemChangeDate($semester)
+ {
+
+ if ($semester->sem_wechsel) {
+ $semchangedate = strftime('%x', $semester->sem_wechsel);
+ } else {
+ $semesterSwitch = (int) Config::get()->SEMESTER_TIME_SWITCH;
+ $currentSem = $semester->beginn - $semesterSwitch * 7 * 24 * 60 * 60;
+ $semchangedate = strftime('%x', $currentSem);
+ }
+
+ return $semchangedate;
+ }
+
+ public static function findDefault()
+ {
+ $all_sems = self::getAll();
+
+ // $all_sems now contents only semesters with valid change dates, either manual or SEMESTER_TIME_SWITCH
+ foreach (array_reverse($all_sems) as $semester) {
+ if ($semester['ende'] <= time()) {
+ continue;
+ }
+
+ if ($semester['sem_wechsel']) {
+ $timestamp = $semester['sem_wechsel'];
+ } else {
+ $timestamp = $semester['beginn'] - (int)Config::get()->SEMESTER_TIME_SWITCH * 7 * 24 * 60 * 60;
+ }
+
+ if ($timestamp <= time()) {
+ return $semester;
+ }
+ }
+ }
+}