Пример #1
0
 /**
  * Configure settings for several courses at once.
  *
  * @param String $set_id course set ID to fetch courses from
  * @param String $csv    export course members to file
  */
 public function configure_courses_action($set_id, $csv = null)
 {
     if (Request::isXhr()) {
         $this->response->add_header('X-Title', _('Ausgewählte Veranstaltungen konfigurieren'));
     }
     $courseset = new CourseSet($set_id);
     $this->set_id = $courseset->getId();
     $this->courses = Course::findMany($courseset->getCourses(), "ORDER BY VeranstaltungsNummer, Name");
     $this->applications = AdmissionPriority::getPrioritiesStats($courseset->getId());
     $distinct_members = array();
     $multi_members = array();
     foreach ($this->courses as $course) {
         $all_members = $course->members->findBy('status', words('user autor'))->pluck('user_id');
         $all_members = array_merge($all_members, $course->admission_applicants->findBy('status', words('accepted awaiting'))->pluck('user_id'));
         $all_members = array_unique($all_members);
         foreach ($all_members as $one) {
             $multi_members[$one]++;
         }
         $distinct_members = array_unique(array_merge($distinct_members, $all_members));
     }
     $multi_members = array_filter($multi_members, function ($a) {
         return $a > 1;
     });
     $this->count_distinct_members = count($distinct_members);
     $this->count_multi_members = count($multi_members);
     if ($csv == 'csv') {
         $captions = array(_("Nummer"), _("Name"), _("versteckt"), _("Zeiten"), _("Dozenten"), _("max. Teilnehmer"), _("Teilnehmer aktuell"), _("Anzahl Anmeldungen"), _("Anzahl Anmeldungen Prio 1"), _("Warteliste"), _("max. Anzahl Warteliste"), _("vorläufige Anmeldung"), _("verbindliche Anmeldung"));
         $data = array();
         foreach ($this->courses as $course) {
             $row = array();
             $row[] = $course->veranstaltungsnummer;
             $row[] = $course->name;
             $row[] = $course->visible ? _("nein") : _("ja");
             $row[] = join('; ', $course->cycles->toString());
             $row[] = join(', ', $course->members->findBy('status', 'dozent')->orderBy('position')->pluck('Nachname'));
             $row[] = $course->admission_turnout;
             $row[] = $course->getNumParticipants();
             $row[] = $this->applications[$course->id]['c'];
             $row[] = $this->applications[$course->id]['h'];
             $row[] = $course->admission_disable_waitlist ? _("nein") : _("ja");
             $row[] = $course->admission_waitlist_max > 0 ? $course->admission_waitlist_max : '';
             $row[] = $course->admission_prelim ? _("ja") : _("nein");
             $row[] = $course->admission_binding ? _("ja") : _("nein");
             $data[] = $row;
         }
         $tmpname = md5(uniqid('tmp'));
         if (array_to_csv($data, $GLOBALS['TMP_PATH'] . '/' . $tmpname, $captions)) {
             $this->redirect(GetDownloadLink($tmpname, 'Veranstaltungen_' . $courseset->getName() . '.csv', 4, 'force'));
             return;
         }
     }
     if (in_array($csv, words('download_all_members download_multi_members'))) {
         $liste = array();
         $multi_members = $all_participants = array();
         foreach ($this->courses as $course) {
             $participants = $course->members->findBy('status', words('user autor'))->toGroupedArray('user_id', words('username vorname nachname email status'));
             $participants += $course->admission_applicants->findBy('status', words('accepted awaiting'))->toGroupedArray('user_id', words('username vorname nachname email status'));
             $all_participants += $participants;
             foreach (array_keys($participants) as $one) {
                 $multi_members[$one][] = $course->name . ($course->veranstaltungsnummer ? '|' . $course->veranstaltungsnummer : '');
             }
             foreach ($participants as $user_id => $part) {
                 $liste[] = array($part['username'], $part['vorname'], $part['nachname'], $part['email'], $course->name . ($course->veranstaltungsnummer ? '|' . $course->veranstaltungsnummer : ''), $part['status']);
             }
         }
         if ($csv == 'download_all_members') {
             $captions = array(_("Username"), _("Vorname"), _("Nachname"), _("Email"), _("Veranstaltung"), _("Status"));
             if (count($liste)) {
                 $tmpname = md5(uniqid('tmp'));
                 if (array_to_csv($liste, $GLOBALS['TMP_PATH'] . '/' . $tmpname, $captions)) {
                     $this->redirect(GetDownloadLink($tmpname, 'Gesamtteilnehmerliste_' . $courseset->getName() . '.csv', 4, 'force'));
                     return;
                 }
             }
         } else {
             $liste = array();
             $multi_members = array_filter($multi_members, function ($a) {
                 return count($a) > 1;
             });
             $c = 0;
             $max_count = array();
             foreach ($multi_members as $user_id => $courses) {
                 $member = $all_participants[$user_id];
                 $liste[$c] = array($member['username'], $member['vorname'], $member['nachname'], $member['email']);
                 foreach ($courses as $one) {
                     $liste[$c][] = $one;
                 }
                 $max_count[] = count($courses);
                 $c++;
             }
             $captions = array(_("Nutzername"), _("Vorname"), _("Nachname"), _("Email"));
             foreach (range(1, max($max_count)) as $num) {
                 $captions[] = _("Veranstaltung") . ' ' . $num;
             }
             if (count($liste)) {
                 $tmpname = md5(uniqid('tmp'));
                 if (array_to_csv($liste, $GLOBALS['TMP_PATH'] . '/' . $tmpname, $captions)) {
                     $this->redirect(GetDownloadLink($tmpname, 'Mehrfachanmeldungen_' . $courseset->getName() . '.csv', 4, 'force'));
                     return;
                 }
             }
         }
     }
     if (Request::submitted('configure_courses_save')) {
         CSRFProtection::verifyUnsafeRequest();
         $admission_turnouts = Request::intArray('configure_courses_turnout');
         $admission_waitlists = Request::intArray('configure_courses_disable_waitlist');
         $admission_waitlists_max = Request::intArray('configure_courses_waitlist_max');
         $admission_bindings = Request::intArray('configure_courses_binding');
         $admission_prelims = Request::intArray('configure_courses_prelim');
         $hidden = Request::intArray('configure_courses_hidden');
         $ok = 0;
         foreach ($this->courses as $course) {
             if ($GLOBALS['perm']->have_studip_perm('admin', $course->id)) {
                 $do_update_admission = $course->admission_turnout < $admission_turnouts[$course->id];
                 $course->admission_turnout = $admission_turnouts[$course->id];
                 $course->admission_disable_waitlist = isset($admission_waitlists[$course->id]) ? 0 : 1;
                 $course->admission_waitlist_max = $course->admission_disable_waitlist ? 0 : $admission_waitlists_max[$course->id];
                 $course->admission_binding = @$admission_bindings[$course->id] ?: 0;
                 $course->admission_prelim = @$admission_prelims[$course->id] ?: 0;
                 $course->visible = @$hidden[$course->id] ? 0 : 1;
                 $ok += $course->store();
                 if ($do_update_admission) {
                     update_admission($course->id);
                 }
             }
         }
         if ($ok) {
             PageLayout::postMessage(MessageBox::success(_("Die zugeordneten Veranstaltungen wurden konfiguriert.")));
         }
         $this->redirect($this->url_for('admission/courseset/configure/' . $courseset->getId()));
         return;
     }
 }
Пример #2
0
 /**
  * Distribute seats for several courses in a course set using the given
  * user priorities.
  *
  * @param CourseSet $courseSet The course set containing the courses
  * that seats shall be distributed for.
  * @see CourseSet
  */
 private function distributeByPriorities($courseSet)
 {
     Log::DEBUG('start seat distribution for course set: ' . $courseSet->getId());
     $limited_admission = $courseSet->getAdmissionRule('LimitedAdmission');
     //all users with their priorities
     $claiming_users = AdmissionPriority::getPriorities($courseSet->getId());
     //all users which have bonus/malus
     $factored_users = $courseSet->getUserFactorList();
     //all users with their max number of courses
     $max_seats_users = array_combine(array_keys($claiming_users), array_map(function ($u) use($limited_admission) {
         return $limited_admission->getMaxNumberForUser($u);
     }, array_keys($claiming_users)));
     //unlucky users get a bonus for the next round
     $bonus_users = array();
     //users / courses für later waitlist distribution
     $waiting_users = array();
     //number of already distributed seats for users
     $distributed_users = array();
     $prio_mapper = function ($users, $course_id) use($claiming_users) {
         $mapper = function ($u) use($course_id) {
             return isset($u[$course_id]) ? $u[$course_id] : null;
         };
         return array_filter(array_map($mapper, array_intersect_key($claiming_users, array_flip($users))));
     };
     //sort courses by highest count of prio 1 applicants
     $stats = AdmissionPriority::getPrioritiesStats($courseSet->getId());
     $courses = array_map(function ($a) {
         return $a['h'];
     }, $stats);
     arsort($courses, SORT_NUMERIC);
     $max_prio = AdmissionPriority::getPrioritiesMax($courseSet->getId());
     //count already manually distributed places
     $distributed_users = $this->countParticipatingUsers(array_keys($courses), array_keys($claiming_users));
     Log::DEBUG('already distributed users: ' . print_r($distributed_users, 1));
     //walk through all prios with all courses
     foreach (range(1, $max_prio) as $current_prio) {
         foreach (array_keys($courses) as $course_id) {
             $current_claiming = array();
             $course = Course::find($course_id);
             $free_seats = $course->getFreeSeats();
             //find users with current prio for this course, if they still need a place
             foreach ($claiming_users as $user_id => $prio_courses) {
                 if ($prio_courses[$course_id] == $current_prio && $distributed_users[$user_id] < $max_seats_users[$user_id]) {
                     //exclude participants
                     if (!$course->getParticipantStatus($user_id)) {
                         $current_claiming[$user_id] = 1;
                         if (isset($factored_users[$user_id])) {
                             $current_claiming[$user_id] *= $factored_users[$user_id];
                         }
                     } else {
                         Log::DEBUG(sprintf('user %s is already %s in course %s, ignoring', $user_id, $course->getParticipantStatus($user_id), $course->id));
                     }
                 }
             }
             //give maximum bonus to users which were unlucky before
             foreach (array_keys($current_claiming) as $user_id) {
                 if ($bonus_users[$user_id] > 0) {
                     $current_claiming[$user_id] = $bonus_users[$user_id] * count($current_claiming) + 1;
                     $bonus_users[$user_id]--;
                 }
             }
             Log::DEBUG(sprintf('distribute %s seats on %s claiming with prio %s in course %s', $free_seats, count($current_claiming), $current_prio, $course->id));
             Log::DEBUG('users to distribute: ' . print_r($current_claiming, 1));
             $current_claiming = $this->rollTheDice($current_claiming);
             Log::DEBUG('the die is cast: ' . print_r($current_claiming, 1));
             $chosen_ones = array_slice(array_keys($current_claiming), 0, $free_seats);
             Log::DEBUG('chosen ones: ' . print_r($chosen_ones, 1));
             $this->addUsersToCourse($chosen_ones, $course, $prio_mapper($chosen_ones, $course->id));
             foreach ($chosen_ones as $one) {
                 $distributed_users[$one]++;
             }
             if ($free_seats < count($current_claiming)) {
                 $remaining_ones = array_slice(array_keys($current_claiming), $free_seats);
                 foreach ($remaining_ones as $one) {
                     $bonus_users[$one]++;
                     $waiting_users[$current_prio][$course_id][] = $one;
                 }
             }
         }
     }
     //distribute to waitlists if applicable
     Log::DEBUG('waiting list: ' . print_r($waiting_users, 1));
     foreach ($waiting_users as $current_prio => $current_prio_waiting_courses) {
         foreach ($current_prio_waiting_courses as $course_id => $users) {
             $users = array_filter($users, function ($user_id) use($distributed_users, $max_seats_users) {
                 return $distributed_users[$user_id] < $max_seats_users[$user_id];
             });
             $course = Course::find($course_id);
             Log::DEBUG(sprintf('distribute waitlist of %s with prio %s in course %s', count($users), $current_prio, $course->id));
             if (!$course->admission_disable_waitlist) {
                 if ($course->admission_waitlist_max) {
                     $free_seats_waitlist = $course->admission_waitlist_max - $course->getNumWaiting();
                     $free_seats_waitlist = $free_seats_waitlist < 0 ? 0 : $free_seats_waitlist;
                 } else {
                     $free_seats_waitlist = count($users);
                 }
                 $waiting_list_ones = array_slice($users, 0, $free_seats_waitlist);
                 Log::DEBUG('waiting list ones: ' . print_r($waiting_list_ones, 1));
                 $this->addUsersToWaitlist($waiting_list_ones, $course, $prio_mapper($waiting_list_ones, $course->id));
                 foreach ($waiting_list_ones as $one) {
                     $distributed_users[$one]++;
                 }
             } else {
                 $free_seats_waitlist = 0;
             }
             if ($free_seats_waitlist < count($users)) {
                 $remaining_ones = array_slice($users, $free_seats_waitlist);
                 Log::DEBUG('remaining ones: ' . print_r($remaining_ones, 1));
                 $this->notifyRemainingUsers($remaining_ones, $course, $prio_mapper($remaining_ones, $course->id));
             }
         }
     }
 }
Пример #3
0
 /**
  * A person applies for a course.
  */
 function apply_action()
 {
     $user_id = $GLOBALS['user']->id;
     $courseset = CourseSet::getSetForCourse($this->course_id);
     $this->course_name = PageLayout::getTitle();
     if ($courseset) {
         $errors = $courseset->checkAdmission($user_id, $this->course_id);
         if (count($errors)) {
             $this->courseset_message = $courseset->toString(true);
             $this->admission_error = MessageBox::error(_("Die Anmeldung war nicht erfolgreich."), $errors);
             foreach ($courseset->getAdmissionRules() as $rule) {
                 $admission_form .= $rule->getInput();
             }
             if ($admission_form) {
                 $this->admission_form = $admission_form;
             }
         } else {
             if ($courseset->isSeatDistributionEnabled()) {
                 if ($courseset->hasAlgorithmRun()) {
                     if ($courseset->getSeatDistributionTime()) {
                         $msg = _("Die Plätze in dieser Veranstaltung wurden automatisch verteilt.");
                     }
                     if (StudipLock::get('enrolment' . $this->course_id)) {
                         $course = Course::find($this->course_id);
                         if ($course->getFreeSeats() && !$course->getNumWaiting()) {
                             $enrol_user = true;
                         } else {
                             if ($course->isWaitlistAvailable()) {
                                 $seminar = new Seminar($course);
                                 if ($seminar->addToWaitlist($user_id, 'last')) {
                                     $msg_details[] = sprintf(_("Alle Plätze sind belegt, Sie wurden daher auf Platz %s der Warteliste gesetzt."), $maxpos);
                                 }
                             } else {
                                 $this->admission_error = MessageBox::error(_("Die Anmeldung war nicht erfolgreich. Alle Plätze sind belegt und es steht keine Warteliste zur Verfügung."));
                             }
                         }
                     } else {
                         $this->admission_error = MessageBox::error(_("Die Anmeldung war wegen technischer Probleme nicht erfolgreich. Bitte versuchen Sie es später noch einmal."));
                     }
                 } else {
                     $msg = _("Die Plätze in dieser Veranstaltung werden automatisch verteilt.");
                     if ($limit = $courseset->getAdmissionRule('LimitedAdmission')) {
                         $msg_details[] = sprintf(_("Diese Veranstaltung gehört zu einem Anmeldeset mit %s Veranstaltungen. Sie können maximal %s davon belegen. Bei der Verteilung werden die von Ihnen gewünschten Prioritäten berücksichtigt."), count($courseset->getCourses()), $limit->getMaxNumber());
                         $this->user_max_limit = $limit->getMaxNumberForUser($user_id);
                         if (get_config('IMPORTANT_SEMNUMBER')) {
                             $order = "ORDER BY VeranstaltungsNummer, Name";
                         } else {
                             $order = "ORDER BY Name";
                         }
                         $this->priocourses = Course::findMany($courseset->getCourses(), $order);
                         $this->user_prio = AdmissionPriority::getPrioritiesByUser($courseset->getId(), $user_id);
                         $this->max_limit = $limit->getMaxNumber();
                         $this->prio_stats = AdmissionPriority::getPrioritiesStats($courseset->getId());
                         $this->already_claimed = count($this->user_prio);
                     } else {
                         $this->priocourses = Course::find($this->course_id);
                         $this->already_claimed = array_key_exists($this->course_id, AdmissionPriority::getPrioritiesByUser($courseset->getId(), $user_id));
                     }
                     $msg_details[] = _("Zeitpunkt der automatischen Verteilung: ") . strftime("%x %X", $courseset->getSeatDistributionTime());
                     $this->num_claiming = count(AdmissionPriority::getPrioritiesByCourse($courseset->getId(), $this->course_id));
                     if ($this->already_claimed) {
                         $msg_details[] = _("Sie sind bereits für die Verteilung angemeldet.");
                     }
                 }
                 $this->courseset_message = MessageBox::info($msg, $msg_details);
             } else {
                 $enrol_user = true;
             }
         }
     } else {
         $enrol_user = true;
     }
     if ($enrol_user) {
         $course = Seminar::GetInstance($this->course_id);
         if ($course->admission_prelim) {
             if (!$course->isStudygroup() && $course->admission_prelim_txt && !Request::submitted('apply')) {
                 $this->admission_prelim_txt = $course->admission_prelim_txt;
                 $this->admission_prelim_comment = Config::get()->ADMISSION_PRELIM_COMMENT_ENABLE;
                 $this->admission_form = $this->render_template_as_string('course/enrolment/prelim');
             } else {
                 if (Request::get('admission_comment')) {
                     $admission_comment = get_fullname() . ': ' . Request::get('admission_comment');
                 } else {
                     $admission_comment = '';
                 }
                 if ($course->addPreliminaryMember($user_id, $admission_comment)) {
                     if ($course->isStudygroup()) {
                         if (StudygroupModel::isInvited($user_id, $this->course_id)) {
                             // an invitation exists, so accept the join request automatically
                             $status = 'autor';
                             StudygroupModel::accept_user(get_username($user_id), $this->course_id);
                             StudygroupModel::cancelInvitation(get_username($user_id), $this->course_id);
                             $success = sprintf(_("Sie wurden in die Veranstaltung %s als %s eingetragen."), $course->getName(), get_title_for_status($status, 1));
                             PageLayout::postMessage(MessageBox::success($success));
                         } else {
                             $success = sprintf(_("Sie wurden auf die Anmeldeliste der Studiengruppe %s eingetragen. Die Moderatoren der Studiengruppe können Sie jetzt freischalten."), $course->getName());
                             PageLayout::postMessage(MessageBox::success($success));
                         }
                     } else {
                         $success = sprintf(_("Sie wurden in die Veranstaltung %s vorläufig eingetragen."), $course->getName());
                         PageLayout::postMessage(MessageBox::success($success));
                     }
                 }
             }
         } else {
             $status = 'autor';
             if ($course->addMember($user_id, $status)) {
                 $success = sprintf(_("Sie wurden in die Veranstaltung %s als %s eingetragen."), $course->getName(), get_title_for_status($status, 1));
                 PageLayout::postMessage(MessageBox::success($success));
                 $this->enrol_user = true;
                 if (StudygroupModel::isInvited($user_id, $this->course_id)) {
                     // delete an existing invitation
                     StudygroupModel::cancelInvitation(get_username($user_id), $this->course_id);
                 }
             }
         }
         unset($this->courseset_message);
     }
     StudipLock::release();
 }