Added event type evaluation and storage of selection bitflag. Fixed some details in the UI
This commit is contained in:
parent
bb13d74849
commit
f1c5bd3cd8
@ -21,12 +21,12 @@
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="10" />
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="28" />
|
||||
|
||||
@ -58,10 +58,10 @@ namespace BreCalClient
|
||||
this.LoginResult.UserEmail = this.textBoxUserEmail.Text.Trim();
|
||||
this.LoginResult.NotifyEmail = this.checkboxEMailNotify.IsChecked ?? false;
|
||||
this.LoginResult.NotifyPopup = this.checkboxPushNotify.IsChecked ?? false;
|
||||
this.LoginResult.NotifyOn.Clear();
|
||||
if ((this.checkListBoxEventSelection.SelectedItems.Count > 0) && (this.LoginResult.NotifyOn == null))
|
||||
this.LoginResult.NotifyOn = new();
|
||||
foreach(NotificationType nt in this.checkListBoxEventSelection.SelectedItems)
|
||||
this.LoginResult.NotifyOn.Clear();
|
||||
foreach (NotificationType nt in this.checkListBoxEventSelection.SelectedItems)
|
||||
this.LoginResult.NotifyOn.Add(nt);
|
||||
this.ChangeUserSettingsRequested?.Invoke();
|
||||
}
|
||||
|
||||
@ -8,24 +8,17 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using ToastNotifications.Core;
|
||||
using BreCalClient.misc.Model;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace BreCalClient
|
||||
{
|
||||
internal class AppNotification
|
||||
internal class AppNotification(int id)
|
||||
{
|
||||
private static readonly Dictionary<int, AppNotification> _notifications = new();
|
||||
private readonly int _id;
|
||||
private static readonly ObservableCollection<AppNotification> _notificationsCollection = new();
|
||||
|
||||
public AppNotification(int id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
private static readonly Dictionary<int, AppNotification> _notifications = [];
|
||||
private static readonly ObservableCollection<AppNotification> _notificationsCollection = [];
|
||||
|
||||
#region Properties
|
||||
|
||||
public int Id { get { return _id; } }
|
||||
public int Id { get { return id; } }
|
||||
|
||||
public string? NotificationType
|
||||
{
|
||||
@ -95,7 +88,7 @@ namespace BreCalClient
|
||||
SaveNotifications();
|
||||
}
|
||||
|
||||
internal static bool UpdateNotifications(List<Notification> notifications, System.Collections.Concurrent.ConcurrentDictionary<int, ShipcallControlModel> currentShipcalls, ToastViewModel vm)
|
||||
internal static bool UpdateNotifications(List<Notification> notifications, System.Collections.Concurrent.ConcurrentDictionary<int, ShipcallControlModel> currentShipcalls, ToastViewModel vm, LoginResult loginResult)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
@ -121,6 +114,10 @@ namespace BreCalClient
|
||||
if (!iAmAssigned) continue;
|
||||
}
|
||||
|
||||
// filter out notifications the user is not interested in
|
||||
if((notification.Type != null) && !loginResult.NotifyOn.Contains(notification.Type.Value))
|
||||
continue;
|
||||
|
||||
if (!_notificationsCollection.Where(x => x.Id == notification.Id).Any())
|
||||
{
|
||||
List<AppNotification> newList = new(_notificationsCollection);
|
||||
@ -176,9 +173,8 @@ namespace BreCalClient
|
||||
if (!string.IsNullOrEmpty(ap.Message))
|
||||
toastText += $" \n{ap.Message}";
|
||||
|
||||
if (!_notifications.ContainsKey(notification.Id))
|
||||
if (_notifications.TryAdd(notification.Id, ap))
|
||||
{
|
||||
_notifications.Add(notification.Id, ap);
|
||||
App.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
vm.ShowAppNotification(toastText, options);
|
||||
@ -197,7 +193,7 @@ namespace BreCalClient
|
||||
internal static void SaveNotifications()
|
||||
{
|
||||
if (Properties.Settings.Default.Notifications == null)
|
||||
Properties.Settings.Default.Notifications = new();
|
||||
Properties.Settings.Default.Notifications = [];
|
||||
else
|
||||
Properties.Settings.Default.Notifications.Clear();
|
||||
foreach (int notification_id in _notifications.Keys)
|
||||
|
||||
@ -52,7 +52,7 @@ namespace BreCalClient
|
||||
|
||||
private readonly ConcurrentDictionary<int, ShipcallControlModel> _allShipcallsDict = new();
|
||||
private readonly ConcurrentDictionary<int, ShipcallControl> _allShipCallsControlDict = new();
|
||||
private readonly List<ShipcallControlModel> _visibleControlModels = new();
|
||||
private readonly List<ShipcallControlModel> _visibleControlModels = [];
|
||||
|
||||
private readonly ShipcallApi _shipcallApi;
|
||||
private readonly UserApi _userApi;
|
||||
@ -195,7 +195,7 @@ namespace BreCalClient
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
if ((ex.ErrorContent != null && ((string)ex.ErrorContent).StartsWith("{"))) {
|
||||
if ((ex.ErrorContent != null && ((string)ex.ErrorContent).StartsWith('{'))) {
|
||||
Error? anError = JsonConvert.DeserializeObject<Error>((string)ex.ErrorContent);
|
||||
if ((anError != null) && anError.ErrorField.Equals("invalid credentials"))
|
||||
this.labelLoginResult.Content = BreCalClient.Resources.Resources.textWrongCredentials;
|
||||
@ -309,7 +309,7 @@ namespace BreCalClient
|
||||
scmOut.Shipcall.DepartureBerthId = esc.ShipcallModel.Shipcall?.ArrivalBerthId;
|
||||
if (esc.ShipcallModel.Shipcall != null)
|
||||
{
|
||||
scmOut.Shipcall.Participants = new();
|
||||
scmOut.Shipcall.Participants = [];
|
||||
scmOut.Shipcall.Participants.AddRange(esc.ShipcallModel.Shipcall.Participants);
|
||||
foreach(ParticipantType pType in esc.ShipcallModel.AssignedParticipants.Keys)
|
||||
scmOut.AssignedParticipants[pType] = esc.ShipcallModel.AssignedParticipants[pType];
|
||||
@ -418,7 +418,7 @@ namespace BreCalClient
|
||||
{
|
||||
this.searchFilterControl.SearchFilter.Ports.Clear();
|
||||
|
||||
List<Berth> berths = new();
|
||||
List<Berth> berths = [];
|
||||
foreach (Port port in comboBoxPorts.SelectedItems)
|
||||
{
|
||||
this.searchFilterControl.SearchFilter.Ports.Add(port.Id);
|
||||
@ -449,8 +449,8 @@ namespace BreCalClient
|
||||
_historyDialog.Closed += (sender, e) => { this._historyDialog = null; };
|
||||
_historyDialog.HistoryItemSelected += (x) =>
|
||||
{
|
||||
if(_allShipCallsControlDict.ContainsKey(x))
|
||||
_allShipCallsControlDict[x].BringIntoView();
|
||||
if(_allShipCallsControlDict.TryGetValue(x, out ShipcallControl? value))
|
||||
value.BringIntoView();
|
||||
};
|
||||
_historyDialog.Show();
|
||||
}
|
||||
@ -514,14 +514,14 @@ namespace BreCalClient
|
||||
SearchFilterModel? currentFilter = null;
|
||||
if (SearchFilterModel.filterMap != null)
|
||||
{
|
||||
if((_loginResult != null) && SearchFilterModel.filterMap.ContainsKey(_loginResult.Id))
|
||||
if((_loginResult != null) && SearchFilterModel.filterMap.TryGetValue(_loginResult.Id, out SearchFilterModel? value))
|
||||
{
|
||||
currentFilter = SearchFilterModel.filterMap[_loginResult.Id];
|
||||
currentFilter = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SearchFilterModel.filterMap = new();
|
||||
SearchFilterModel.filterMap = [];
|
||||
}
|
||||
if (currentFilter == null)
|
||||
{
|
||||
@ -607,7 +607,7 @@ namespace BreCalClient
|
||||
// load times for each shipcall
|
||||
List<Times> currentTimes = await _timesApi.TimesGetAsync(shipcall.Id);
|
||||
|
||||
if (!_allShipcallsDict.ContainsKey(shipcall.Id))
|
||||
if (!_allShipcallsDict.TryGetValue(shipcall.Id, out ShipcallControlModel? value))
|
||||
{
|
||||
// add entry
|
||||
ShipcallControlModel scm = new()
|
||||
@ -619,10 +619,9 @@ namespace BreCalClient
|
||||
}
|
||||
else
|
||||
{
|
||||
// update entry
|
||||
_allShipcallsDict[shipcall.Id].Shipcall = shipcall;
|
||||
_allShipcallsDict[shipcall.Id].Times = currentTimes;
|
||||
UpdateShipcall(_allShipcallsDict[shipcall.Id]);
|
||||
value.Shipcall = shipcall;
|
||||
value.Times = currentTimes;
|
||||
UpdateShipcall(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -674,7 +673,7 @@ namespace BreCalClient
|
||||
if (_loginResult?.NotifyPopup ?? false)
|
||||
{
|
||||
List<Notification> notifications = await _staticApi.NotificationsGetAsync();
|
||||
AppNotification.UpdateNotifications(notifications, _allShipcallsDict, _vm);
|
||||
AppNotification.UpdateNotifications(notifications, _allShipcallsDict, _vm, _loginResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -689,8 +688,8 @@ namespace BreCalClient
|
||||
_allShipcallsDict[scm.Shipcall.Id] = scm;
|
||||
|
||||
Shipcall shipcall = scm.Shipcall;
|
||||
if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId))
|
||||
scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship;
|
||||
if (BreCalLists.ShipLookupDict.TryGetValue(shipcall.ShipId, out ShipModel? value))
|
||||
scm.Ship = value.Ship;
|
||||
|
||||
if (shipcall.Type == ShipcallType.Arrival)
|
||||
{
|
||||
@ -723,8 +722,8 @@ namespace BreCalClient
|
||||
{
|
||||
if(scm.Shipcall == null) return;
|
||||
Shipcall shipcall = scm.Shipcall;
|
||||
if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId))
|
||||
scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship;
|
||||
if (BreCalLists.ShipLookupDict.TryGetValue(shipcall.ShipId, out ShipModel? value))
|
||||
scm.Ship = value.Ship;
|
||||
|
||||
if (shipcall.Type == ShipcallType.Arrival)
|
||||
{
|
||||
@ -969,10 +968,10 @@ namespace BreCalClient
|
||||
foreach (ShipcallControlModel visibleModel in this._visibleControlModels)
|
||||
{
|
||||
if (visibleModel.Shipcall == null) continue; // should not happen
|
||||
if (this._allShipCallsControlDict.ContainsKey(visibleModel.Shipcall.Id))
|
||||
if (this._allShipCallsControlDict.TryGetValue(visibleModel.Shipcall.Id, out ShipcallControl? value))
|
||||
{
|
||||
this._allShipCallsControlDict[visibleModel.Shipcall.Id].RefreshData();
|
||||
this.stackPanel.Children.Add(this._allShipCallsControlDict[visibleModel.Shipcall.Id]);
|
||||
value.RefreshData();
|
||||
this.stackPanel.Children.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1122,8 +1121,8 @@ namespace BreCalClient
|
||||
}
|
||||
else
|
||||
{
|
||||
if(editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY))
|
||||
editControl.Times.ParticipantId = editControl.ShipcallModel.AssignedParticipants[ParticipantType.AGENCY].ParticipantId;
|
||||
if(editControl.ShipcallModel.AssignedParticipants.TryGetValue(ParticipantType.AGENCY, out ParticipantAssignment? value))
|
||||
editControl.Times.ParticipantId = value.ParticipantId;
|
||||
}
|
||||
editControl.Times.ParticipantType = (int)ParticipantType.AGENCY;
|
||||
if(editControl.ShowDialog() ?? false)
|
||||
@ -1139,9 +1138,9 @@ namespace BreCalClient
|
||||
}
|
||||
|
||||
// always try to be the agent, even if we are BSMD
|
||||
if (editControl.ShipcallModel.AssignedParticipants.ContainsKey(ParticipantType.AGENCY))
|
||||
if (editControl.ShipcallModel.AssignedParticipants.TryGetValue(ParticipantType.AGENCY, out ParticipantAssignment? value))
|
||||
{
|
||||
editControl.Times.ParticipantId = editControl.ShipcallModel.AssignedParticipants[ParticipantType.AGENCY].ParticipantId;
|
||||
editControl.Times.ParticipantId = value.ParticipantId;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1190,7 +1189,7 @@ namespace BreCalClient
|
||||
// (if the special-flag is enabled). Assigned Agency: ShipcallParticipantMap(id=628, shipcall_id=115, participant_id=10,
|
||||
// type=8, created=datetime.datetime(2024, 8, 28, 15, 13, 14), modified=None) with Flags: 42\"}
|
||||
|
||||
Match m = Regex.Match(message, "\\{(.*)\\}");
|
||||
Match m = ErrorRegex().Match(message);
|
||||
if ((m != null) && m.Success)
|
||||
{
|
||||
try
|
||||
@ -1235,6 +1234,9 @@ namespace BreCalClient
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
[GeneratedRegex("\\{(.*)\\}")]
|
||||
private static partial Regex ErrorRegex();
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
@ -595,4 +595,7 @@
|
||||
<data name="textShipcallCancelled" xml:space="preserve">
|
||||
<value>Der Anlauf wurde storniert</value>
|
||||
</data>
|
||||
<data name="textNotifyOn" xml:space="preserve">
|
||||
<value>Benachrichtigung bei</value>
|
||||
</data>
|
||||
</root>
|
||||
@ -2,8 +2,7 @@ from flask import Blueprint, request
|
||||
from ..schemas import model
|
||||
from .. import impl
|
||||
from ..services.auth_guard import auth_guard
|
||||
import json
|
||||
import logging
|
||||
|
||||
from marshmallow import ValidationError
|
||||
from . import verify_if_request_is_json
|
||||
from BreCal.validators.validation_error import create_dynamic_exception_response, create_validation_error_response
|
||||
|
||||
@ -237,7 +237,7 @@ class SQLQuery():
|
||||
@staticmethod
|
||||
def get_user()->str:
|
||||
query = "SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, " +\
|
||||
"api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, created, modified FROM user " +\
|
||||
"api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, notify_event, created, modified FROM user " +\
|
||||
"WHERE user_name = ?username? OR user_email = ?username?"
|
||||
return query
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ from .. import local_db
|
||||
from ..services import jwt_handler
|
||||
from BreCal.database.sql_queries import SQLQuery
|
||||
|
||||
|
||||
def GetUser(options):
|
||||
|
||||
try:
|
||||
@ -18,7 +19,7 @@ def GetUser(options):
|
||||
# query = SQLQuery.get_user()
|
||||
# data = commands.query(query, model=model.User, param={"username" : options["username"]})
|
||||
data = commands.query("SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, " +
|
||||
"api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, created, modified FROM user " +
|
||||
"api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, notify_event, created, modified FROM user " +
|
||||
"WHERE user_name = ?username? OR user_email = ?username?",
|
||||
model=model.User, param={"username" : options["username"]})
|
||||
|
||||
@ -35,7 +36,8 @@ def GetUser(options):
|
||||
"notify_email": data[0].notify_email,
|
||||
"notify_whatsapp": data[0].notify_whatsapp,
|
||||
"notify_signal": data[0].notify_signal,
|
||||
"notify_popup": data[0].notify_popup
|
||||
"notify_popup": data[0].notify_popup,
|
||||
"notify_on": model.bitflag_to_list(data[0].notify_event)
|
||||
}
|
||||
token = jwt_handler.generate_jwt(payload=result, lifetime=120) # generate token valid 60 mins
|
||||
result["token"] = token # add token to user data
|
||||
|
||||
@ -35,7 +35,7 @@ def PutUser(schemaModel):
|
||||
# should this be refactored?
|
||||
# Also, what about the 'user_name'?
|
||||
# 'participant_id' would also not trigger an update in isolation
|
||||
if "first_name" in schemaModel or "last_name" in schemaModel or "user_phone" in schemaModel or "user_email" in schemaModel or "notify_email" in schemaModel or "notify_whatsapp" in schemaModel or "notify_signal" in schemaModel or "notify_popup" in schemaModel:
|
||||
if "first_name" in schemaModel or "last_name" in schemaModel or "user_phone" in schemaModel or "user_email" in schemaModel or "notify_email" in schemaModel or "notify_whatsapp" in schemaModel or "notify_signal" in schemaModel or "notify_popup" in schemaModel or "notify_on" in schemaModel:
|
||||
# query = SQLQuery.get_user_put(schemaModel)
|
||||
query = "UPDATE user SET "
|
||||
isNotFirst = False
|
||||
@ -49,7 +49,14 @@ def PutUser(schemaModel):
|
||||
if isNotFirst:
|
||||
query += ", "
|
||||
isNotFirst = True
|
||||
|
||||
if key != "notify_on":
|
||||
query += key + " = ?" + key + "? "
|
||||
else:
|
||||
flag_value = model.list_to_bitflag(schemaModel["notify_on"])
|
||||
query += "notify_event = " + str(flag_value) + " "
|
||||
|
||||
|
||||
|
||||
query += "WHERE id = ?id?"
|
||||
affected_rows = commands.execute(query, param=schemaModel)
|
||||
|
||||
@ -10,11 +10,12 @@ from typing import List
|
||||
import json
|
||||
import re
|
||||
import datetime
|
||||
|
||||
from BreCal.validators.time_logic import validate_time_is_in_not_too_distant_future
|
||||
from BreCal.validators.validation_base_utils import check_if_string_has_special_characters
|
||||
from BreCal.database.enums import ParticipantType, ParticipantFlag
|
||||
|
||||
# from BreCal. ... import check_if_user_is_bsmd_type
|
||||
|
||||
|
||||
def obj_dict(obj):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
@ -84,6 +85,21 @@ class NotificationType(IntEnum):
|
||||
def _missing_(cls, value):
|
||||
return cls.undefined
|
||||
|
||||
def bitflag_to_list(bitflag: int) -> list[NotificationType]:
|
||||
if bitflag is None:
|
||||
return []
|
||||
"""Converts an integer bitflag to a list of NotificationType enums."""
|
||||
return [nt for nt in NotificationType if bitflag & (1 << (nt.value - 1))]
|
||||
|
||||
def list_to_bitflag(notifications: fields.List) -> int:
|
||||
"""Converts a list of NotificationType enums to an integer bitflag."""
|
||||
try:
|
||||
iter(notifications)
|
||||
return sum(1 << (nt.value - 1) for nt in notifications)
|
||||
except TypeError as te:
|
||||
return 0
|
||||
|
||||
|
||||
class ShipcallType(IntEnum):
|
||||
undefined = 0
|
||||
arrival = 1
|
||||
@ -497,6 +513,7 @@ class UserSchema(Schema):
|
||||
notify_whatsapp = fields.Bool(allow_none=True, required=False)
|
||||
notify_signal = fields.Bool(allow_none=True, required=False)
|
||||
notify_popup = fields.Bool(allow_none=True, required=False)
|
||||
notify_on = fields.List(fields.Enum(NotificationType), required=False, allow_none=True)
|
||||
|
||||
@validates("user_phone")
|
||||
def validate_user_phone(self, value):
|
||||
@ -507,7 +524,7 @@ class UserSchema(Schema):
|
||||
|
||||
@validates("user_email")
|
||||
def validate_user_email(self, value):
|
||||
if value and not re.match(r"[^@]+@[^@]+\.[^@]+", value) in value:
|
||||
if value and not re.match(r"[^@]+@[^@]+\.[^@]+", value):
|
||||
raise ValidationError({"user_email":f"invalid email address"})
|
||||
|
||||
|
||||
@ -556,10 +573,15 @@ class User:
|
||||
notify_popup: bool
|
||||
created: datetime
|
||||
modified: datetime
|
||||
ports: List[NotificationType] = field(default_factory=list)
|
||||
notify_event: List[NotificationType] = field(default_factory=list)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(id)
|
||||
|
||||
def wants_notification(self, notification_type: NotificationType):
|
||||
return notification_type in self.notify_event
|
||||
|
||||
@dataclass
|
||||
class Ship:
|
||||
id: int
|
||||
|
||||
@ -229,14 +229,14 @@ def SendNotifications():
|
||||
users = users_dict[notification.participant_id]
|
||||
for user in users:
|
||||
# send notification to user
|
||||
if user.notify_email:
|
||||
if user.notify_email and user.wants_notifications(notification.type):
|
||||
if user not in email_dict:
|
||||
email_dict[user] = []
|
||||
email_dict[user].append(notification)
|
||||
if user.notify_whatsapp:
|
||||
if user.notify_whatsapp and user.wants_notifications(notification.type):
|
||||
# TBD
|
||||
pass
|
||||
if user.notify_signal:
|
||||
if user.notify_signal and user.wants_notifications(notification.type):
|
||||
# TBD
|
||||
pass
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ def get_user_simple():
|
||||
notify_signal = True
|
||||
notify_popup = True
|
||||
|
||||
|
||||
user = User(
|
||||
user_id,
|
||||
participant_id,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user