Files
cwsvJudo/homepage/participo/pmelo.inc.php
2024-02-04 19:32:30 +01:00

403 lines
14 KiB
PHP

<?php
setlocale(LC_ALL, "de_DE@euro", "de_DE", "de", "ge");
set_include_path(get_include_path() . PATH_SEPARATOR . "./lib/");
// Configs
require_once "config/participo.php";
require_once $config["basePath"] . "/config/cwsvJudo.config.php";
// Libs
require_once "participoLib/user.php";
participo::init($cwsvJudoConfig);
$pmelo = new PmElo();
class PmElo
{
/** initialize the pmelo session */
function __construct()
{
// starting/continuing a session
session_start();
// create 'pmelo' section in the sessions data
if (!array_key_exists("pmelo", $_SESSION)) {
$_SESSION["pmelo"] = [];
}
// - data storage for id-s of chosen fighters
if (!array_key_exists("fighterIds", $_SESSION["pmelo"])) {
$_SESSION["pmelo"]["fighterIds"] = [];
}
// load/recreate ranking data
if (!is_file(self::$pmeloJson)) {
$this->log["info"][] =
"Couldn't find `" . self::$pmeloJson . "`. Create a new one!";
file_put_contents(self::$pmeloJson, json_encode([]));
}
// init members
$this->trainees = self::getTrainees();
$this->rankings = $this->createRankings();
$this->fighters = [];
foreach ($_SESSION["pmelo"]["fighterIds"] as $id) {
$this->fighters[$id] = $this->trainees[$id];
}
// @todo Can be split. Some actions need the members to be already filled, some could be done earlier.
// check for action in post data
if (array_key_exists("pmelo", $_POST)) {
// setting the current fighters
// - meaning: fighter list is automatically updated by posted fighters
if (array_key_exists("fighterIds", $_POST["pmelo"])) {
$_SESSION["pmelo"]["fighterIds"] =
$_POST["pmelo"]["fighterIds"];
header("Location: pmelo");
}
if (array_key_exists("promote", $_POST["pmelo"])) {
$promote = $_POST["pmelo"]["promote"];
$this->promote(
$promote["promoteeId"],
$promote["promoteeRank"],
$promote["degradeeId"],
$promote["degradeeRank"]
);
header("Location: pmelo");
}
}
// save the updated ranking
// @todo should be in destructor
self::saveRankings($this->rankings);
}
////
// html code injection
///
/** Echo the fighterInputForm */
public function htmlFighterInputForm(){
echo($this->getFighterInputForm($this->trainees));
}
/** Echo the fighterTable */
public function htmlFighterTable(){
echo($this->getFighterTable($this->fighters, $this->rankings));
}
/** Echo logOutput */
public function htmlLogOutput(){
echo($this->getLogOutput($this->log));
}
////
// saving and loading
////
/** load all active trainees into a id=>User assoc array */
static private function getTrainees()
{
$traineeList = array_map(
"User::fromDbArray",
User::dbSelectWithAttribute(4)
);
$traineeAssocArray = [];
foreach ($traineeList as $trainee) {
$traineeAssocArray[$trainee->getId()] = $trainee;
}
return $traineeAssocArray;
}
/** load ranking from json file */
static private function loadRankings()
{
return json_decode(file_get_contents(self::$pmeloJson));
}
/** save a ranking to json file */
static private function saveRankings(array $rankings)
{
file_put_contents(self::$pmeloJson, json_encode($rankings));
}
////
// member functions
////
/** create the ranking for the current session
*
* - load saved ranking from file
* - remove old trainees
* - add new trainees
*/
private function createRankings()
{
// load the last state of the ranking
$loadedRanking = self::loadRankings();
// check if the ranked trainees still are in training
$cleanRanking = [];
// - trainees that aren't currently in the ranking for later inserting
$toBeInsertedTrainees = $this->trainees;
foreach ($loadedRanking as $id) {
if (array_key_exists($id, $this->trainees)) {
// userId is still in training, so append it to the cleaned up ranking
$cleanRanking[] = $id;
// trainees already in the ranking we won't have to insert manual
unset($toBeInsertedTrainees[$id]);
}
}
// get the ids of the to be inserted trainees sorted by the birthday
$newTraineesIds = array_map(function ($u) {
return $u->getId();
}, $toBeInsertedTrainees);
usort($newTraineesIds, function ($lhs, $rhs) {
return $this->trainees[$lhs]->getStrBirthday() <
$this->trainees[$rhs]->getStrBirthday()
? -1
: ($this->trainees[$lhs]->getStrBirthday() >
$this->trainees[$rhs]->getStrBirthday()
? 1
: 0);
});
// now we do a mergesort (step)
$updatedRanking = [];
$idxNewTrainees = count($newTraineesIds) - 1;
$idxRanking = count($cleanRanking) - 1;
// - while we can merge, we merge
while ($idxRanking >= 0 && $idxNewTrainees >= 0) {
if (
$this->trainees[$cleanRanking[$idxRanking]]->getDateOfBirth() >
$this->trainees[
$newTraineesIds[$idxNewTrainees]
]->getDateOfBirth()
) {
array_unshift($updatedRanking, $cleanRanking[$idxRanking]);
$idxRanking -= 1;
} else {
array_unshift(
$updatedRanking,
$newTraineesIds[$idxNewTrainees]
);
$idxNewTrainees -= 1;
}
}
while ($idxRanking >= 0) {
array_unshift($updatedRanking, $cleanRanking[$idxRanking]);
$idxRanking -= 1;
}
while ($idxNewTrainees >= 0) {
array_unshift($updatedRanking, $newTraineesIds[$idxNewTrainees]);
$idxNewTrainees -= 1;
}
return $updatedRanking;
}
/** promote a fighter
*
* Promotion means two fighter switch places
*
* $promoteeId (int>0) id to promote
* $promopteeRank (int>0) current rank of the one to be promoted
* $degradeeId (int>0) id to demote
* $degradeeRank (int>0) new rank of the promotee
*/
private function promote(
int $promoteeId,
int $promoteeRank,
int $degradeeId,
int $degradeeRank
) {
// input sanitation
filterId($promoteeId);
filterId($promoteeRank);
filterId($degradeeId);
filterId($degradeeRank);
// check if both id are in the current fighter list
if (
array_key_exists($promoteeId, $this->fighters) &&
array_key_exists($degradeeId, $this->fighters)
) {
// check their current rank
if (
$this->rankings[$promoteeRank] == $promoteeId &&
$this->rankings[$degradeeRank] == $degradeeId
) {
// @todo check if they are rank neighbors in the fighter list
if (true) {
// do the actual switch
$this->rankings[$promoteeRank] = $degradeeId;
$this->rankings[$degradeeRank] = $promoteeId;
}
}
}
// save the updated ranking
$this->saveRankings($this->rankings);
}
////
// helper for output generation
////
/** Assemble the Input form for the fighter selection
*
* $trainees (array(participo::user)) list of trainees that can be selected for fighting
*
* return fighterInputForm as html code
*/
static private function getFighterInputForm(array $trainees){
$style = [
'classes'=>[
'input-field' => "input-field col s12"
]
];
$retHtml =
'<form id="pmelo-form-fighters" action="pmelo" method="post">'
// selects have to be wrapped into an .input-field, limitation of materializeCss
.'<div class='.$style['classes']['input-field'].'>'
.'<select name="pmelo[fighterIds][]" id="pmelo-form-fighters-select" multiple>'
.'<option value="" disabled selected>Kämpfer auswählen</option>';
foreach ($trainees as $trainee) {
$retHtml .=
'<option value="'.$trainee->getId().'">'.
$trainee->getFirstname().' '.$trainee->getName().
'</option>';
}
$retHtml .=
'</select>'.
'<label for="pmelo-form-fighters-select">'.
'Kämpfer auswählen'.
'</label>'.
'<input type="submit" value="Liste erstellen">'.
'</div>'.
'</form>';
return $retHtml;
}
/** Assemble the ranking table of the fighters
*
* $fighters (array(id=>participo::User)) list of fighters in the active round
* $ranking (array(rank=>id)) ranking of the current selected fighters
*
* return fighter ranking table as html code
*/
static private function getFighterTable(array $fighters, array $rankings){
$retHtml =
'<table>'.
'<thead><tr><th>Platz</th><th>Name</th><th>Aufstieg</th></tr></thead>'.
'<tbody>';
if (!empty($fighters)) {
// cache the predecessor in the ranking for the promoting form
$lastOne = null;
foreach ($rankings as $rank => $id) {
if (array_key_exists($id, $fighters)) {
$fighter = $fighters[$id];
$retHtml .= '<tr>'.
'<td>'.$rank.'</td>'.
'<td>'.join(' ', [$fighter->getFirstName(), $fighter->getName()]).'</td>';
if (!is_null($lastOne)){
$retHtml .= '<td>'.self::getPromoteButton(
$fighter->getId(),$rank,
$lastOne["id"],$lastOne["rank"]
).'</td>';
}
$retHtml .= '</tr>';
$lastOne = ["id" => $fighter->getId(), "rank" => $rank];
}
}
}
$retHtml .=
'</tbody>'.
'</table>';
return $retHtml;
}
/** Assemble a Promote button
*
* $promoteeId (int>0) id to promote
* $promoteeRank (int>0) current rank of the promotee
* $degradeeId (int>0) id to demote
* $degradeeRank (int>0) rank of the to be demoted
*/
static private function getPromoteButton(
int $promoteeId,
int $promoteeRank,
int $degradeeId,
int $degradeeRank
) {
return "<form action=\"pmelo\" method=\"post\">" .
"<input type= \"hidden\" name=\"pmelo[promote][promoteeId]\" value=\"" . $promoteeId . "\" />" .
"<input type= \"hidden\" name=\"pmelo[promote][promoteeRank]\" value=\"" . $promoteeRank . "\" />" .
"<input type= \"hidden\" name=\"pmelo[promote][degradeeId]\" value=\"" . $degradeeId . "\" />" .
"<input type= \"hidden\" name=\"pmelo[promote][degradeeRank]\" value=\"" . $degradeeRank . "\">" .
"<input class=\"btn\" type=\"submit\" value=\"^\">".
"</form>";
}
/** Assemble the log Output
*
* $log (array logLevel=>array(messages)) lists of log messages per logLevel
*
* return logOutput as html code
*/
private static function getLogOutput(array $log){
$retHtml =
'<ul class="collection with-header">'.
'<li class="collection-header"><h3>Logs</h3></li>';
foreach ($log as $logLevel => $messages) {
if (!empty($messages)) {
$retHtml .=
'<li class="collection-item">'.
'<ul class="collection with-header">'.
'<li class="collection-header">'.$logLevel.'</li>';
foreach ($messages as $message) {
$retHtml .=
'<li>'.$message.'</li>';
}
$retHtml .=
'</ul>'.
'</li>';
}
}
return $retHtml;
}
/** simple logger to a logging buffer */
private function var_log(
$variable,
string $prefix = null,
string $logChannel = "info"
) {
if (!in_array($logChannel, ["info", "warning", "error"])) {
$logChannel = "info";
}
$prefix = !empty($prefix) ? $prefix . ": " : "";
$this->log[$logChannel][] = $prefix . var_export($variable, true);
}
////
// member variables
////
/** current chosen trainees for fighting [id=>participo::User] */
public $fighters = null;
/** current trainees available for fighting as [id=>participo::User] */
public $trainees = null;
/** the current ranking as [rank=>id] */
public $rankings = null;
/** a place to save log messages for later output */
public $log = ["error" => [], "warning" => [], "info" => []];
static $pmeloJson = "./pmeloRanking.json";
}