Merge pull request #464 from allerta-vvf/master

Alert improvements
This commit is contained in:
Matteo Gheza 2022-03-14 00:21:44 +01:00 committed by GitHub
commit 84a7c08c0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 247 additions and 90 deletions

View File

@ -8,31 +8,47 @@ final class NotEnoughAvailableUsersException extends Exception {}
function callsList($type) {
global $db;
$crew = [];
if ($type == 'full') {
} else if ($type == 'support') {
if($db->selectValue("SELECT COUNT(id) FROM `allerta01_profiles` WHERE `available` = 1") < 2) {
throw new NotEnoughAvailableUsersException();
return;
}
$chief_result = $db->selectRow("SELECT * FROM `".DB_PREFIX."_profiles` WHERE `hidden` = 0 AND `available` = 1 AND `chief` = 1 ORDER BY services ASC, trainings DESC, availability_minutes ASC, name ASC LIMIT 1");
if(is_null($chief_result)) {
throw new NoChiefAvailableException();
if($db->selectValue("SELECT COUNT(id) FROM `".DB_PREFIX."_profiles` WHERE `available` = 1") < 2) {
throw new NotEnoughAvailableUsersException();
return;
}
$chief_result = $db->selectRow("SELECT * FROM `".DB_PREFIX."_profiles` WHERE `hidden` = 0 AND `available` = 1 AND `chief` = 1 ORDER BY services ASC, trainings DESC, availability_minutes ASC, name ASC LIMIT 1");
if(is_null($chief_result)) {
throw new NoChiefAvailableException();
return;
}
$crew[] = $chief_result;
if($chief_result["driver"]) {
$result = $db->select("SELECT * FROM `".DB_PREFIX."_profiles` WHERE `hidden` = 0 AND `available` = 1 ORDER BY chief ASC, services ASC, trainings DESC, availability_minutes ASC, name ASC");
foreach ($result as $row) {
if(!in_array($row["id"], array_column($crew, 'id'))) {
$crew[] = $row;
}
}
} else {
$driver_result = $db->selectRow("SELECT * FROM `".DB_PREFIX."_profiles` WHERE `hidden` = 0 AND `available` = 1 AND `driver` = 1 ORDER BY chief ASC, services ASC, trainings DESC, availability_minutes ASC, name ASC");
if(is_null($driver_result)) {
throw new NoDriverAvailableException();
return;
}
$crew[] = $chief_result;
if($chief_result["driver"]) {
$result = $db->selectRow("SELECT * FROM `".DB_PREFIX."_profiles` WHERE `hidden` = 0 AND `available` = 1 ORDER BY chief ASC, services ASC, trainings DESC, availability_minutes ASC, name ASC LIMIT 1");
$crew[] = $result;
} else {
$driver_result = $db->selectRow("SELECT * FROM `".DB_PREFIX."_profiles` WHERE `hidden` = 0 AND `available` = 1 AND `driver` = 1 ORDER BY chief ASC, services ASC, trainings DESC, availability_minutes ASC, name ASC LIMIT 1");
if(is_null($driver_result)) {
throw new NoDriverAvailableException();
return;
foreach ($driver_result as $row) {
if(!in_array($row["id"], array_column($crew, 'id'))) {
$crew[] = $row;
}
$crew[] = $driver_result;
}
}
if ($type == 'full') {
$result = $db->select("SELECT * FROM `".DB_PREFIX."_profiles` WHERE `hidden` = 0 AND `available` = 1 ORDER BY chief ASC, services ASC, trainings DESC, availability_minutes ASC, name ASC");
foreach ($result as $row) {
if(!in_array($row["id"], array_column($crew, 'id'))) {
$crew[] = $row;
}
}
}
return $crew;
}
@ -46,37 +62,167 @@ function loadCrewMemberData($input) {
return array_merge($input, $result);
}
function updateAlertMessages($alert, $crew=null, $alertDeleted = false) {
global $Bot, $users, $db;
if(is_null($Bot)) initializeBot(NONE);
if(is_null($crew)) {
$crew = json_decode($alert["crew"], true);
}
$notification_messages = json_decode($alert["notification_messages"], true);
$notification_text = generateAlertReportMessage($alert["type"], $crew, $alert["enabled"], $alert["notes"], $alert["created_by"], $alertDeleted);
foreach($notification_messages as $chat_id => $message_id) {
try {
$Bot->editMessageText([
"chat_id" => $chat_id,
"message_id" => $message_id,
"text" => $notification_text
]);
} catch(skrtdev\Telegram\BadRequestException) {
//
}
}
if($alertDeleted) {
foreach($crew as &$member) {
$message_id = $member["telegram_message_id"];
$chat_id = $member["telegram_chat_id"];
if(!is_null($message_id) && !is_null($chat_id)) {
$Bot->sendMessage([
"chat_id" => $chat_id,
"text" => "Allerta rimossa.\nPartecipazione non più richiesta.",
"reply_to_message_id" => $message_id
]);
try {
$Bot->editMessageReplyMarkup([
"chat_id" => $chat_id,
"message_id" => $message_id,
"reply_markup" => [
'inline_keyboard' => [
]
]
]);
} catch(skrtdev\Telegram\BadRequestException) {
//
}
}
}
return;
}
$available_users_count = 0;
$drivers_count = 0;
$chiefs_count = 0;
foreach($crew as &$member) {
if($member["response"] === true) {
$user = $users->getUserById($member["id"]);
$available_users_count++;
if($user["driver"]) $drivers_count++;
if($user["chief"]) $chiefs_count++;
}
}
if(
($alert["type"] === "support" && $available_users_count >= 2 && $chiefs_count >= 1 && $drivers_count >= 1) ||
($alert["type"] === "full" && $available_users_count >= 5 && $chiefs_count >= 1 && $drivers_count >= 1)
) {
$db->update(
DB_PREFIX."_alerts",
[
"enabled" => 0
],
[
"id" => $alert["id"]
]
);
$notification_text = generateAlertReportMessage($alert["type"], $crew, false, $alert["notes"], $alert["created_by"], $alertDeleted);
foreach($notification_messages as $chat_id => $message_id) {
try {
$Bot->editMessageText([
"chat_id" => $chat_id,
"message_id" => $message_id,
"text" => $notification_text
]);
} catch(skrtdev\Telegram\BadRequestException) {
//
}
}
foreach($crew as &$member) {
$message_id = $member["telegram_message_id"];
$chat_id = $member["telegram_chat_id"];
if((!is_null($message_id) || !is_null($chat_id)) && $member["response"] === "waiting") {
$Bot->sendMessage([
"chat_id" => $chat_id,
"text" => "Numero minimo vigili richiesti raggiunto.\nPartecipazione non più richiesta.",
"reply_to_message_id" => $message_id
]);
try {
$Bot->editMessageReplyMarkup([
"chat_id" => $chat_id,
"message_id" => $message_id,
"reply_markup" => [
'inline_keyboard' => [
]
]
]);
} catch(skrtdev\Telegram\BadRequestException) {
//
}
}
}
}
}
function setAlertResponse($response, $userId, $alertId) {
global $db, $Bot;
global $db, $users, $Bot;
if(is_null($Bot)) initializeBot(NONE);
$alert = $db->selectRow(
"SELECT * FROM `".DB_PREFIX."_alerts` WHERE `id` = ?", [$alertId]
);
if(!$alert["enabled"]) return;
$crew = json_decode($alert["crew"], true);
$messageText = $response ? "🟢 Partecipazione accettata." : "🔴 Partecipazione rifiutata.";
foreach($crew as &$member) {
if($member["id"] == $userId) {
if($member["response"] === $response) return;
$message_id = $member["telegram_message_id"];
$chat_id = $member["telegram_chat_id"];
$Bot->sendMessage([
"chat_id" => $chat_id,
"text" => $messageText,
"reply_to_message_id" => $message_id
]);
$Bot->editMessageReplyMarkup([
"chat_id" => $chat_id,
"message_id" => $message_id,
"reply_markup" => [
'inline_keyboard' => [
]
]
]);
if(!is_null($message_id) || !is_null($chat_id)) {
$Bot->sendMessage([
"chat_id" => $chat_id,
"text" => $messageText,
"reply_to_message_id" => $message_id
]);
try {
$Bot->editMessageReplyMarkup([
"chat_id" => $chat_id,
"message_id" => $message_id,
"reply_markup" => [
'inline_keyboard' => [
]
]
]);
} catch(skrtdev\Telegram\BadRequestException) {
//
}
}
$member["response"] = $response;
$member["response_time"] = get_timestamp();
break;
}
}
$db->update(
@ -89,15 +235,7 @@ function setAlertResponse($response, $userId, $alertId) {
]
);
$notification_messages = json_decode($alert["notification_messages"], true);
$notification_text = generateAlertReportMessage($alert["type"], $crew);
foreach($notification_messages as $chat_id => $message_id) {
$Bot->editMessageText([
"chat_id" => $chat_id,
"message_id" => $message_id,
"text" => $notification_text
]);
}
updateAlertMessages($alert, $crew);
}
function alertsRouter (FastRoute\RouteCollector $r) {
@ -159,7 +297,7 @@ function alertsRouter (FastRoute\RouteCollector $r) {
];
}
$notifications = sendAlertReportMessage($_POST["type"], $crew);
$notifications = sendAlertReportMessage($_POST["type"], $crew, true, "", $users->auth->getUserId());
$db->insert(
DB_PREFIX."_alerts",
@ -174,7 +312,7 @@ function alertsRouter (FastRoute\RouteCollector $r) {
$alertId = $db->getLastInsertId();
foreach($crew as &$member) {
list($member["telegram_message_id"], $member["telegram_chat_id"]) = sendAlertRequestMessage($_POST["type"], $member["id"], $alertId);
[$member["telegram_message_id"], $member["telegram_chat_id"]] = sendAlertRequestMessage($_POST["type"], $member["id"], $alertId, "", $users->auth->getUserId());
}
$db->update(
@ -233,6 +371,14 @@ function alertsRouter (FastRoute\RouteCollector $r) {
"id" => $vars["id"]
]
);
$alert = $db->selectRow(
"SELECT * FROM `".DB_PREFIX."_alerts` WHERE `id` = :id",
[
":id" => $vars["id"]
]
);
updateAlertMessages($alert);
}
);
@ -256,6 +402,14 @@ function alertsRouter (FastRoute\RouteCollector $r) {
"id" => $vars["id"]
]
);
$alert = $db->selectRow(
"SELECT * FROM `".DB_PREFIX."_alerts` WHERE `id` = :id",
[
":id" => $vars["id"]
]
);
updateAlertMessages($alert, null, true);
}
);
}

View File

@ -110,15 +110,31 @@ function sendTelegramNotificationToUser($message, $userId, $options = [])
}
}
function generateAlertReportMessage($alertType, $crew) {
function generateAlertMessage($alertType, $alertEnabled, $alertNotes, $alertCreatedBy, $alertDeleted=false) {
global $users;
$message =
"<b><i><u>Allertamento in corso:</u></i></b> ".
($alertType === "full" ? "Richiesta <b>squadra completa 🚒</b>" : "<b>Supporto 🧯</b>\n").
"Squadra:\n";
"<b><i><u>".($alertEnabled ? "Allertamento in corso" : ($alertDeleted ? "Allertamento completato" : "Allerta rimossa")).":</u></i></b> ".
($alertType === "full" ? "Richiesta <b>squadra completa 🚒</b>" : "<b>Supporto 🧯</b>\n");
if(!is_null($alertNotes) && $alertNotes !== "") {
$message .= "Note:\n<b>".$alertNotes."</b>\n";
}
if(!is_null($alertCreatedBy)) {
$message .= "Lanciata da: <b>".$users->getName($alertCreatedBy)."</b>\n";
}
return $message;
}
function generateAlertReportMessage($alertType, $crew, $alertEnabled, $alertNotes, $alertCreatedBy, $alertDeleted=false) {
global $users;
$message = generateAlertMessage($alertType, $alertEnabled, $alertNotes, $alertCreatedBy);
$message .= "\nSquadra:\n";
foreach($crew as $member) {
if((!$alertEnabled || $alertDeleted) && $member["response"] === "waiting") continue;
$user = $users->getUserById($member['id']);
$message .= "<i>".$user["name"]."</i> ";
if($user["chief"]) $message .= "CS";
@ -133,40 +149,28 @@ function generateAlertReportMessage($alertType, $crew) {
}
$message .= "\n";
}
return $message;
}
function generateAlertRequestMessage($alertType, $live=true) {
$message =
"<b><i><u>". ($live ? "Allertamento in corso" : "Notifica di allertamento ricevuta") .":</u></i></b> ".
($alertType === "full" ? "Richiesta <b>squadra completa 🚒</b>" : "<b>Supporto 🧯</b>\n");
return $message;
}
function sendAlertReportMessage($alertType, $crew) {
global $Bot;
$message = generateAlertReportMessage($alertType, $crew);
function sendAlertReportMessage($alertType, $crew, $alertEnabled, $alertNotes, $alertCreatedBy, $alertDeleted = false) {
$message = generateAlertReportMessage($alertType, $crew, $alertEnabled, $alertNotes, $alertCreatedBy, $alertDeleted);
return sendTelegramNotification($message, false);
}
function sendAlertRequestMessage($alertType, $userId, $alertId) {
global $Bot;
return sendTelegramNotificationToUser(generateAlertRequestMessage($alertType), $userId, [
function sendAlertRequestMessage($alertType, $userId, $alertId, $alertNotes, $alertCreatedBy, $alertDeleted = false) {
return sendTelegramNotificationToUser(generateAlertMessage($alertType, true, $alertNotes, $alertCreatedBy, $alertDeleted), $userId, [
'reply_markup' => [
'inline_keyboard' => [
[
[
'text' => '✅ Partecipo',
'callback_data' => "alert_yes_".$alertType."_".$alertId
'callback_data' => "alert_yes_".$alertId
],
[
'text' => 'Non partecipo ❌',
'callback_data' => "alert_no_".$alertType."_".$alertId
'callback_data' => "alert_no_".$alertId
]
]
]
@ -331,20 +335,9 @@ function telegramBotRouter() {
if(strpos($callback_query->data, 'alert_') === 0) {
$data = explode("_", str_replace("alert_", "", $callback_query->data));
$alert_type = $data[1];
$alert_id = $data[2];
$alert_id = $data[1];
setAlertResponse($data[0] === "yes", getUserIdByFrom($user->id), $alert_id);
/*
if($data[0] === "yes") {
$callback_query->answer(" Partecipazione registrata con successo.");
$message->reply("🟢 Partecipazione accettata.");
} else if($data[0] === "no") {
$callback_query->answer(" Rifiuto alla partecipazione registrato con successo.");
$message->reply("🔴 Partecipazione rifiutata.");
}
$message->editReplyMarkup([]);
*/
return;
}
});

View File

@ -18,7 +18,8 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users">
<ng-container *ngFor="let user of users">
<tr *ngIf="alertEnabled || user.response !== 'waiting'">
<td>
<img alt="red helmet" src="./assets/icons/red_helmet.png" width="20px" *ngIf="user.chief">
<img alt="red helmet" src="./assets/icons/black_helmet.png" width="20px" *ngIf="!user.chief">
@ -38,9 +39,10 @@
<td>Non presente</td>
</ng-container>
</tr>
</ng-container>
</tbody>
</table>
<ng-container *ngIf="auth.profile.hasRole('SUPER_EDITOR')">
<ng-container *ngIf="auth.profile.hasRole('SUPER_EDITOR') && alertEnabled">
<button type="button" class="btn btn-primary mb-2" (click)="isAdvancedCollapsed = !isAdvancedCollapsed"
[attr.aria-expanded]="!isAdvancedCollapsed" aria-controls="collapseBasic">
<ng-container *ngIf="isAdvancedCollapsed">Mostra impostazioni avanzate</ng-container>
@ -50,11 +52,11 @@
<div class="well well-lg card card-block card-header">
<label for="details" class="form-label">Dettagli allerta</label>
<textarea class="form-control" id="details" rows="3" [(ngModel)]="notes"></textarea>
<button class="btn btn-secondary mt-2" (click)="saveAlertSettings()" *ngIf="auth.profile.hasRole('SUPER_EDITOR')">Salva</button>
<button class="btn btn-secondary mt-2" (click)="saveAlertSettings()">Salva</button>
</div>
</div>
</ng-container>
<ng-container *ngIf="!auth.profile.hasRole('SUPER_EDITOR') && notes !== ''">
<ng-container *ngIf="(!auth.profile.hasRole('SUPER_EDITOR') && notes !== '') || !alertEnabled">
<div class="well well-lg card card-block card-header">
<h5>Dettagli allerta</h5>
<h2>{{ notes }}</h2>
@ -62,6 +64,6 @@
</ng-container>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" (click)="deleteAlert()" *ngIf="auth.profile.hasRole('SUPER_EDITOR')">Rimuovi allerta corrente <i class="fas fa-exclamation-triangle"></i></button>
<button type="button" class="btn btn-danger" (click)="deleteAlert()" *ngIf="auth.profile.hasRole('SUPER_EDITOR') && alertEnabled">Rimuovi allerta corrente <i class="fas fa-exclamation-triangle"></i></button>
<button type="button" class="btn btn-secondary" (click)="bsModalRef.hide()">{{ 'close'|translate }}</button>
</div>

View File

@ -22,6 +22,8 @@ export class ModalAlertComponent implements OnInit, OnDestroy {
notes = "";
alertEnabled = true;
constructor(
public bsModalRef: BsModalRef,
private api: ApiClientService,
@ -31,9 +33,9 @@ export class ModalAlertComponent implements OnInit, OnDestroy {
loadResponsesData() {
this.api.get(`alerts/${this.id}`).then((response) => {
console.log(this.users !== response.crew, this.users, response.crew);
if(this.alertEnabled !== response.enabled) this.alertEnabled = response.enabled;
if(!isEqual(this.users, response.crew)) this.users = response.crew;
if (response.notes !== "" && response.notes !== null) {
if (this.notes === "" || this.notes === null) {
if(!isEqual(this.notes, response.notes)) this.notes = response.notes;
}
});

View File

@ -20,10 +20,10 @@
<owner-image></owner-image>
<div class="text-center" *ngIf="auth.profile.hasRole('SUPER_EDITOR')">
<div class="btn-group" role="group">
<button type="button" class="btn btn-danger" (click)="addAlertFull()" [disabled]="!api?.availableUsers || api.availableUsers! < 5">
<button type="button" class="btn btn-danger" (click)="addAlertFull()" [disabled]="!api?.availableUsers || api.availableUsers! < 5 || alertLoading">
🚒 Richiedi squadra completa
</button>
<button type="button" class="btn btn-warning" (click)="addAlertSupport()" [disabled]="!api?.availableUsers || api.availableUsers! < 2">
<button type="button" class="btn btn-warning" (click)="addAlertSupport()" [disabled]="!api?.availableUsers || api.availableUsers! < 2 || alertLoading">
Richiedi squadra di supporto 🧯
</button>
</div>

View File

@ -23,6 +23,8 @@ export class ListComponent implements OnInit, OnDestroy {
public available: boolean | undefined = undefined;
public manual_mode: boolean | undefined = undefined;
public alertLoading = false;
constructor(
public api: ApiClientService,
public auth: AuthService,
@ -72,10 +74,12 @@ export class ListComponent implements OnInit, OnDestroy {
}
addAlertFull() {
this.alertLoading = true;
if(!this.auth.profile.hasRole('SUPER_EDITOR')) return;
this.api.post("alerts", {
type: "full"
}).then((response) => {
this.alertLoading = false;
if(response?.status === "error") {
this.toastr.error(response.message, undefined, {
timeOut: 5000
@ -92,10 +96,12 @@ export class ListComponent implements OnInit, OnDestroy {
}
addAlertSupport() {
this.alertLoading = true;
if(!this.auth.profile.hasRole('SUPER_EDITOR')) return;
this.api.post("alerts", {
type: "support"
}).then((response) => {
this.alertLoading = false;
if(response?.status === "error") {
this.toastr.error(response.message, undefined, {
timeOut: 5000