Added event type evaluation and storage of selection bitflag. Fixed some details in the UI

This commit is contained in:
Daniel Schick 2025-02-05 19:24:07 +01:00
parent bb13d74849
commit f1c5bd3cd8
12 changed files with 99 additions and 67 deletions

View File

@ -21,12 +21,12 @@
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="10" /> <RowDefinition Height="10" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="10" /> <RowDefinition Height="10" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />
<RowDefinition Height="28" /> <RowDefinition Height="28" />

View File

@ -57,11 +57,11 @@ namespace BreCalClient
this.LoginResult.UserPhone = this.textBoxUserPhone.Text.Trim(); this.LoginResult.UserPhone = this.textBoxUserPhone.Text.Trim();
this.LoginResult.UserEmail = this.textBoxUserEmail.Text.Trim(); this.LoginResult.UserEmail = this.textBoxUserEmail.Text.Trim();
this.LoginResult.NotifyEmail = this.checkboxEMailNotify.IsChecked ?? false; this.LoginResult.NotifyEmail = this.checkboxEMailNotify.IsChecked ?? false;
this.LoginResult.NotifyPopup = this.checkboxPushNotify.IsChecked ?? false; this.LoginResult.NotifyPopup = this.checkboxPushNotify.IsChecked ?? false;
this.LoginResult.NotifyOn.Clear();
if ((this.checkListBoxEventSelection.SelectedItems.Count > 0) && (this.LoginResult.NotifyOn == null)) if ((this.checkListBoxEventSelection.SelectedItems.Count > 0) && (this.LoginResult.NotifyOn == null))
this.LoginResult.NotifyOn = new(); 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.LoginResult.NotifyOn.Add(nt);
this.ChangeUserSettingsRequested?.Invoke(); this.ChangeUserSettingsRequested?.Invoke();
} }

View File

@ -8,24 +8,17 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using ToastNotifications.Core; using ToastNotifications.Core;
using BreCalClient.misc.Model; using BreCalClient.misc.Model;
using System.Runtime.CompilerServices;
namespace BreCalClient namespace BreCalClient
{ {
internal class AppNotification internal class AppNotification(int id)
{ {
private static readonly Dictionary<int, AppNotification> _notifications = new(); private static readonly Dictionary<int, AppNotification> _notifications = [];
private readonly int _id; private static readonly ObservableCollection<AppNotification> _notificationsCollection = [];
private static readonly ObservableCollection<AppNotification> _notificationsCollection = new();
public AppNotification(int id)
{
_id = id;
}
#region Properties #region Properties
public int Id { get { return _id; } } public int Id { get { return id; } }
public string? NotificationType public string? NotificationType
{ {
@ -95,7 +88,7 @@ namespace BreCalClient
SaveNotifications(); 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; bool result = false;
@ -121,6 +114,10 @@ namespace BreCalClient
if (!iAmAssigned) continue; 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()) if (!_notificationsCollection.Where(x => x.Id == notification.Id).Any())
{ {
List<AppNotification> newList = new(_notificationsCollection); List<AppNotification> newList = new(_notificationsCollection);
@ -176,9 +173,8 @@ namespace BreCalClient
if (!string.IsNullOrEmpty(ap.Message)) if (!string.IsNullOrEmpty(ap.Message))
toastText += $" \n{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(() => App.Current.Dispatcher.Invoke(() =>
{ {
vm.ShowAppNotification(toastText, options); vm.ShowAppNotification(toastText, options);
@ -197,7 +193,7 @@ namespace BreCalClient
internal static void SaveNotifications() internal static void SaveNotifications()
{ {
if (Properties.Settings.Default.Notifications == null) if (Properties.Settings.Default.Notifications == null)
Properties.Settings.Default.Notifications = new(); Properties.Settings.Default.Notifications = [];
else else
Properties.Settings.Default.Notifications.Clear(); Properties.Settings.Default.Notifications.Clear();
foreach (int notification_id in _notifications.Keys) foreach (int notification_id in _notifications.Keys)

View File

@ -52,7 +52,7 @@ namespace BreCalClient
private readonly ConcurrentDictionary<int, ShipcallControlModel> _allShipcallsDict = new(); private readonly ConcurrentDictionary<int, ShipcallControlModel> _allShipcallsDict = new();
private readonly ConcurrentDictionary<int, ShipcallControl> _allShipCallsControlDict = 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 ShipcallApi _shipcallApi;
private readonly UserApi _userApi; private readonly UserApi _userApi;
@ -195,7 +195,7 @@ namespace BreCalClient
} }
catch (ApiException ex) 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); Error? anError = JsonConvert.DeserializeObject<Error>((string)ex.ErrorContent);
if ((anError != null) && anError.ErrorField.Equals("invalid credentials")) if ((anError != null) && anError.ErrorField.Equals("invalid credentials"))
this.labelLoginResult.Content = BreCalClient.Resources.Resources.textWrongCredentials; this.labelLoginResult.Content = BreCalClient.Resources.Resources.textWrongCredentials;
@ -309,7 +309,7 @@ namespace BreCalClient
scmOut.Shipcall.DepartureBerthId = esc.ShipcallModel.Shipcall?.ArrivalBerthId; scmOut.Shipcall.DepartureBerthId = esc.ShipcallModel.Shipcall?.ArrivalBerthId;
if (esc.ShipcallModel.Shipcall != null) if (esc.ShipcallModel.Shipcall != null)
{ {
scmOut.Shipcall.Participants = new(); scmOut.Shipcall.Participants = [];
scmOut.Shipcall.Participants.AddRange(esc.ShipcallModel.Shipcall.Participants); scmOut.Shipcall.Participants.AddRange(esc.ShipcallModel.Shipcall.Participants);
foreach(ParticipantType pType in esc.ShipcallModel.AssignedParticipants.Keys) foreach(ParticipantType pType in esc.ShipcallModel.AssignedParticipants.Keys)
scmOut.AssignedParticipants[pType] = esc.ShipcallModel.AssignedParticipants[pType]; scmOut.AssignedParticipants[pType] = esc.ShipcallModel.AssignedParticipants[pType];
@ -418,7 +418,7 @@ namespace BreCalClient
{ {
this.searchFilterControl.SearchFilter.Ports.Clear(); this.searchFilterControl.SearchFilter.Ports.Clear();
List<Berth> berths = new(); List<Berth> berths = [];
foreach (Port port in comboBoxPorts.SelectedItems) foreach (Port port in comboBoxPorts.SelectedItems)
{ {
this.searchFilterControl.SearchFilter.Ports.Add(port.Id); this.searchFilterControl.SearchFilter.Ports.Add(port.Id);
@ -449,8 +449,8 @@ namespace BreCalClient
_historyDialog.Closed += (sender, e) => { this._historyDialog = null; }; _historyDialog.Closed += (sender, e) => { this._historyDialog = null; };
_historyDialog.HistoryItemSelected += (x) => _historyDialog.HistoryItemSelected += (x) =>
{ {
if(_allShipCallsControlDict.ContainsKey(x)) if(_allShipCallsControlDict.TryGetValue(x, out ShipcallControl? value))
_allShipCallsControlDict[x].BringIntoView(); value.BringIntoView();
}; };
_historyDialog.Show(); _historyDialog.Show();
} }
@ -514,14 +514,14 @@ namespace BreCalClient
SearchFilterModel? currentFilter = null; SearchFilterModel? currentFilter = null;
if (SearchFilterModel.filterMap != 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 else
{ {
SearchFilterModel.filterMap = new(); SearchFilterModel.filterMap = [];
} }
if (currentFilter == null) if (currentFilter == null)
{ {
@ -607,7 +607,7 @@ namespace BreCalClient
// load times for each shipcall // load times for each shipcall
List<Times> currentTimes = await _timesApi.TimesGetAsync(shipcall.Id); List<Times> currentTimes = await _timesApi.TimesGetAsync(shipcall.Id);
if (!_allShipcallsDict.ContainsKey(shipcall.Id)) if (!_allShipcallsDict.TryGetValue(shipcall.Id, out ShipcallControlModel? value))
{ {
// add entry // add entry
ShipcallControlModel scm = new() ShipcallControlModel scm = new()
@ -619,10 +619,9 @@ namespace BreCalClient
} }
else else
{ {
// update entry value.Shipcall = shipcall;
_allShipcallsDict[shipcall.Id].Shipcall = shipcall; value.Times = currentTimes;
_allShipcallsDict[shipcall.Id].Times = currentTimes; UpdateShipcall(value);
UpdateShipcall(_allShipcallsDict[shipcall.Id]);
} }
} }
@ -674,7 +673,7 @@ namespace BreCalClient
if (_loginResult?.NotifyPopup ?? false) if (_loginResult?.NotifyPopup ?? false)
{ {
List<Notification> notifications = await _staticApi.NotificationsGetAsync(); 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; _allShipcallsDict[scm.Shipcall.Id] = scm;
Shipcall shipcall = scm.Shipcall; Shipcall shipcall = scm.Shipcall;
if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId)) if (BreCalLists.ShipLookupDict.TryGetValue(shipcall.ShipId, out ShipModel? value))
scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship; scm.Ship = value.Ship;
if (shipcall.Type == ShipcallType.Arrival) if (shipcall.Type == ShipcallType.Arrival)
{ {
@ -723,8 +722,8 @@ namespace BreCalClient
{ {
if(scm.Shipcall == null) return; if(scm.Shipcall == null) return;
Shipcall shipcall = scm.Shipcall; Shipcall shipcall = scm.Shipcall;
if (BreCalLists.ShipLookupDict.ContainsKey(shipcall.ShipId)) if (BreCalLists.ShipLookupDict.TryGetValue(shipcall.ShipId, out ShipModel? value))
scm.Ship = BreCalLists.ShipLookupDict[shipcall.ShipId].Ship; scm.Ship = value.Ship;
if (shipcall.Type == ShipcallType.Arrival) if (shipcall.Type == ShipcallType.Arrival)
{ {
@ -969,10 +968,10 @@ namespace BreCalClient
foreach (ShipcallControlModel visibleModel in this._visibleControlModels) foreach (ShipcallControlModel visibleModel in this._visibleControlModels)
{ {
if (visibleModel.Shipcall == null) continue; // should not happen 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(); value.RefreshData();
this.stackPanel.Children.Add(this._allShipCallsControlDict[visibleModel.Shipcall.Id]); this.stackPanel.Children.Add(value);
} }
} }
} }
@ -1122,8 +1121,8 @@ namespace BreCalClient
} }
else else
{ {
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;
} }
editControl.Times.ParticipantType = (int)ParticipantType.AGENCY; editControl.Times.ParticipantType = (int)ParticipantType.AGENCY;
if(editControl.ShowDialog() ?? false) if(editControl.ShowDialog() ?? false)
@ -1139,9 +1138,9 @@ namespace BreCalClient
} }
// always try to be the agent, even if we are BSMD // 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 else
{ {
@ -1190,7 +1189,7 @@ namespace BreCalClient
// (if the special-flag is enabled). Assigned Agency: ShipcallParticipantMap(id=628, shipcall_id=115, participant_id=10, // (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\"} // 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) if ((m != null) && m.Success)
{ {
try try
@ -1235,7 +1234,10 @@ namespace BreCalClient
e.Handled = true; e.Handled = true;
} }
[GeneratedRegex("\\{(.*)\\}")]
private static partial Regex ErrorRegex();
#endregion #endregion
} }
} }

View File

@ -595,4 +595,7 @@
<data name="textShipcallCancelled" xml:space="preserve"> <data name="textShipcallCancelled" xml:space="preserve">
<value>Der Anlauf wurde storniert</value> <value>Der Anlauf wurde storniert</value>
</data> </data>
<data name="textNotifyOn" xml:space="preserve">
<value>Benachrichtigung bei</value>
</data>
</root> </root>

View File

@ -2,8 +2,7 @@ from flask import Blueprint, request
from ..schemas import model from ..schemas import model
from .. import impl from .. import impl
from ..services.auth_guard import auth_guard from ..services.auth_guard import auth_guard
import json
import logging
from marshmallow import ValidationError from marshmallow import ValidationError
from . import verify_if_request_is_json from . import verify_if_request_is_json
from BreCal.validators.validation_error import create_dynamic_exception_response, create_validation_error_response from BreCal.validators.validation_error import create_dynamic_exception_response, create_validation_error_response
@ -16,11 +15,11 @@ def PutUser():
try: try:
verify_if_request_is_json(request) verify_if_request_is_json(request)
content = request.get_json(force=True) content = request.get_json(force=True)
loadedModel = model.UserSchema().load(data=content, many=False, partial=True) loadedModel = model.UserSchema().load(data=content, many=False, partial=True)
return impl.user.PutUser(loadedModel) return impl.user.PutUser(loadedModel)
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex=ex, status_code=400) return create_validation_error_response(ex=ex, status_code=400)

View File

@ -237,7 +237,7 @@ class SQLQuery():
@staticmethod @staticmethod
def get_user()->str: def get_user()->str:
query = "SELECT id, participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash, " +\ 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?" "WHERE user_name = ?username? OR user_email = ?username?"
return query return query

View File

@ -8,6 +8,7 @@ from .. import local_db
from ..services import jwt_handler from ..services import jwt_handler
from BreCal.database.sql_queries import SQLQuery from BreCal.database.sql_queries import SQLQuery
def GetUser(options): def GetUser(options):
try: try:
@ -18,7 +19,7 @@ def GetUser(options):
# query = SQLQuery.get_user() # query = SQLQuery.get_user()
# data = commands.query(query, model=model.User, param={"username" : options["username"]}) # 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, " + 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?", "WHERE user_name = ?username? OR user_email = ?username?",
model=model.User, param={"username" : options["username"]}) model=model.User, param={"username" : options["username"]})
@ -35,7 +36,8 @@ def GetUser(options):
"notify_email": data[0].notify_email, "notify_email": data[0].notify_email,
"notify_whatsapp": data[0].notify_whatsapp, "notify_whatsapp": data[0].notify_whatsapp,
"notify_signal": data[0].notify_signal, "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 token = jwt_handler.generate_jwt(payload=result, lifetime=120) # generate token valid 60 mins
result["token"] = token # add token to user data result["token"] = token # add token to user data

View File

@ -35,7 +35,7 @@ def PutUser(schemaModel):
# should this be refactored? # should this be refactored?
# Also, what about the 'user_name'? # Also, what about the 'user_name'?
# 'participant_id' would also not trigger an update in isolation # '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 = SQLQuery.get_user_put(schemaModel)
query = "UPDATE user SET " query = "UPDATE user SET "
isNotFirst = False isNotFirst = False
@ -49,7 +49,14 @@ def PutUser(schemaModel):
if isNotFirst: if isNotFirst:
query += ", " query += ", "
isNotFirst = True isNotFirst = True
query += key + " = ?" + key + "? "
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?" query += "WHERE id = ?id?"
affected_rows = commands.execute(query, param=schemaModel) affected_rows = commands.execute(query, param=schemaModel)

View File

@ -10,11 +10,12 @@ from typing import List
import json import json
import re import re
import datetime import datetime
from BreCal.validators.time_logic import validate_time_is_in_not_too_distant_future 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.validators.validation_base_utils import check_if_string_has_special_characters
from BreCal.database.enums import ParticipantType, ParticipantFlag from BreCal.database.enums import ParticipantType, ParticipantFlag
# from BreCal. ... import check_if_user_is_bsmd_type
def obj_dict(obj): def obj_dict(obj):
if isinstance(obj, datetime.datetime): if isinstance(obj, datetime.datetime):
@ -84,6 +85,21 @@ class NotificationType(IntEnum):
def _missing_(cls, value): def _missing_(cls, value):
return cls.undefined 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): class ShipcallType(IntEnum):
undefined = 0 undefined = 0
arrival = 1 arrival = 1
@ -497,6 +513,7 @@ class UserSchema(Schema):
notify_whatsapp = fields.Bool(allow_none=True, required=False) notify_whatsapp = fields.Bool(allow_none=True, required=False)
notify_signal = 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_popup = fields.Bool(allow_none=True, required=False)
notify_on = fields.List(fields.Enum(NotificationType), required=False, allow_none=True)
@validates("user_phone") @validates("user_phone")
def validate_user_phone(self, value): def validate_user_phone(self, value):
@ -507,7 +524,7 @@ class UserSchema(Schema):
@validates("user_email") @validates("user_email")
def validate_user_email(self, value): 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"}) raise ValidationError({"user_email":f"invalid email address"})
@ -556,10 +573,15 @@ class User:
notify_popup: bool notify_popup: bool
created: datetime created: datetime
modified: datetime modified: datetime
ports: List[NotificationType] = field(default_factory=list)
notify_event: List[NotificationType] = field(default_factory=list)
def __hash__(self): def __hash__(self):
return hash(id) return hash(id)
def wants_notification(self, notification_type: NotificationType):
return notification_type in self.notify_event
@dataclass @dataclass
class Ship: class Ship:
id: int id: int

View File

@ -229,14 +229,14 @@ def SendNotifications():
users = users_dict[notification.participant_id] users = users_dict[notification.participant_id]
for user in users: for user in users:
# send notification to user # send notification to user
if user.notify_email: if user.notify_email and user.wants_notifications(notification.type):
if user not in email_dict: if user not in email_dict:
email_dict[user] = [] email_dict[user] = []
email_dict[user].append(notification) email_dict[user].append(notification)
if user.notify_whatsapp: if user.notify_whatsapp and user.wants_notifications(notification.type):
# TBD # TBD
pass pass
if user.notify_signal: if user.notify_signal and user.wants_notifications(notification.type):
# TBD # TBD
pass pass

View File

@ -18,18 +18,19 @@ def get_user_simple():
created = datetime.datetime.now() created = datetime.datetime.now()
modified = created+datetime.timedelta(seconds=10) modified = created+datetime.timedelta(seconds=10)
notify_email = True notify_email = True
notify_whatsapp = True notify_whatsapp = True
notify_signal = True notify_signal = True
notify_popup = True notify_popup = True
user = User( user = User(
user_id, user_id,
participant_id, participant_id,
first_name, first_name,
last_name, last_name,
user_name, user_name,
user_email, user_email,
user_phone, user_phone,
password_hash, password_hash,