Select Git revision
Semester.class.php
Forked from
Stud.IP / Stud.IP
Source project has a limited visibility.
-
David Siegfried authored
Closes #2356 Merge request studip/studip!1540
David Siegfried authoredCloses #2356 Merge request studip/studip!1540
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Semester.class.php 16.35 KiB
<?php
/**
* Semester.class.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 semester_id database column
* @property string id alias column for semester_id
* @property string name database column
* @property string description database column
* @property string semester_token database column
* @property string beginn database column
* @property string ende database column
* @property string vorles_beginn database column
* @property string vorles_ende database column
* @property string first_sem_week computed column
* @property string last_sem_week computed column
* @property string past computed column
*/
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']['description'] = 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 = StudipCacheFactory::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 = StudipCacheFactory::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()
{
$dow = (int)date('w', $this->vorles_beginn);
// Date is already on a monday
if ($dow === 1) {
return $this->vorles_beginn;
}
// Saturday or sunday: return next monday
if ($dow === 0 || $dow === 6) {
return strtotime('next monday', $this->vorles_beginn);
}
// Otherwise return last monday
return strtotime('last 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()
{
StudipCacheFactory::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;
}
}
}
}