Merge branch 'feature/extra_time_logic' into release/1.5.0
This commit is contained in:
commit
4870c81783
@ -8,8 +8,8 @@
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<StartupObject>BreCalClient.App</StartupObject>
|
||||
<AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile>
|
||||
<AssemblyVersion>1.5.0.8</AssemblyVersion>
|
||||
<FileVersion>1.5.0.8</FileVersion>
|
||||
<AssemblyVersion>1.5.0.12</AssemblyVersion>
|
||||
<FileVersion>1.5.0.12</FileVersion>
|
||||
<Title>Bremen calling client</Title>
|
||||
<Description>A Windows WPF client for the Bremen calling API.</Description>
|
||||
<ApplicationIcon>containership.ico</ApplicationIcon>
|
||||
|
||||
@ -204,26 +204,22 @@ namespace BreCalClient
|
||||
{
|
||||
case ShipcallType.Departure:
|
||||
isEnabled &= this.comboBoxDepartureBerth.SelectedItem != null;
|
||||
isEnabled &= this.datePickerETD.Value.HasValue;
|
||||
if(this.datePickerETD.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETD.Value.Value > DateTime.Now);
|
||||
isEnabled &= this.datePickerETD.Value.HasValue;
|
||||
isEnabled &= !(this.datePickerETD.Value.IsTooOld() && this.datePickerETD.Value != this.ShipcallModel.Shipcall?.Etd);
|
||||
isEnabled &= !this.datePickerETD.Value.IsTooFar();
|
||||
break;
|
||||
case ShipcallType.Arrival:
|
||||
isEnabled &= this.comboBoxArrivalBerth.SelectedItem != null;
|
||||
isEnabled &= this.datePickerETA.Value.HasValue;
|
||||
if(this.datePickerETA.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETA.Value.Value > DateTime.Now);
|
||||
isEnabled &= this.datePickerETA.Value.HasValue;
|
||||
isEnabled &= !(this.datePickerETA.Value.IsTooOld() && this.datePickerETA.Value != this.ShipcallModel.Shipcall?.Eta);
|
||||
isEnabled &= !this.datePickerETA.Value.IsTooFar();
|
||||
break;
|
||||
case ShipcallType.Shifting:
|
||||
isEnabled &= ((this.comboBoxDepartureBerth.SelectedItem != null) && (this.comboBoxArrivalBerth.SelectedItem != null));
|
||||
isEnabled &= this.datePickerETD.Value.HasValue;
|
||||
isEnabled &= this.datePickerETA.Value.HasValue;
|
||||
if (this.datePickerETD.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETD.Value.Value > DateTime.Now);
|
||||
if (this.datePickerETA.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETA.Value.Value > DateTime.Now);
|
||||
isEnabled &= this.datePickerETA.Value.HasValue;
|
||||
isEnabled &= !(this.datePickerETD.Value.IsTooOld() && this.datePickerETD.Value != this.ShipcallModel.Shipcall?.Etd);
|
||||
isEnabled &= !(this.datePickerETA.Value.IsTooOld() && this.datePickerETA.Value != this.ShipcallModel.Shipcall?.Eta);
|
||||
if (this.datePickerETA.Value.HasValue && this.datePickerETD.Value.HasValue)
|
||||
isEnabled &= (this.datePickerETA.Value.Value > this.datePickerETD.Value.Value);
|
||||
isEnabled &= !this.datePickerETD.Value.IsTooFar();
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
using BreCalClient.misc.Model;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Windows;
|
||||
using static BreCalClient.Extensions;
|
||||
|
||||
@ -97,47 +96,41 @@ namespace BreCalClient
|
||||
{
|
||||
message = "";
|
||||
|
||||
if (this.datePickerETA.Value.HasValue && (this.datePickerETA.Value.Value < DateTime.Now) && (this.datePickerETA_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.datePickerETA_End.Value.HasValue && this.datePickerETA_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.datePickerETA.Value.HasValue && this.datePickerETA_End.Value.HasValue && this.datePickerETA.Value > this.datePickerETA_End.Value)
|
||||
if ((this.datePickerETA.Value != this.Times.EtaBerth) || (this.datePickerETA_End.Value != this.Times.EtaIntervalEnd)) // something has changed
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
if (datePickerETA.Value.IsTooOld() || datePickerETA_End.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETA.Value.HasValue && this.datePickerETA_End.Value.HasValue && this.datePickerETA.Value > this.datePickerETA_End.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && (this.datePickerTidalWindowFrom.Value.Value < DateTime.Now) && (this.datePickerTidalWindowTo.Value == null))
|
||||
if((this.datePickerTidalWindowFrom.Value != this.ShipcallModel.Shipcall?.TidalWindowFrom) || (this.datePickerTidalWindowTo.Value != this.ShipcallModel.Shipcall?.TidalWindowTo)) // something has changed
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
if(datePickerTidalWindowTo.Value.IsTooOld() || this.datePickerTidalWindowFrom.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowTo.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowFrom.Value > this.datePickerTidalWindowTo.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowFrom.Value > this.datePickerTidalWindowTo.Value)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textEndValueBeforeStartValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if((this.datePickerTidalWindowFrom.Value.HasValue && !this.datePickerTidalWindowTo.Value.HasValue) || (!this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTidalBothValues;
|
||||
return false;
|
||||
}
|
||||
if ((this.datePickerTidalWindowFrom.Value.HasValue && !this.datePickerTidalWindowTo.Value.HasValue) || (!this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTidalBothValues;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(this.datePickerETA.Value.IsTooFar() || this.datePickerETA_End.Value.IsTooFar() || this.datePickerTidalWindowFrom.Value.IsTooFar() || this.datePickerTidalWindowTo.Value.IsTooFar())
|
||||
{
|
||||
@ -145,8 +138,7 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if((this.datePickerETA_End.Value.HasValue && !this.datePickerETA.Value.HasValue) ||
|
||||
(this.datePickerTidalWindowTo.Value.HasValue && !this.datePickerTidalWindowFrom.Value.HasValue))
|
||||
if((this.datePickerETA_End.Value.HasValue && !this.datePickerETA.Value.HasValue) || (this.datePickerTidalWindowTo.Value.HasValue && !this.datePickerTidalWindowFrom.Value.HasValue))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textStartTimeMissing;
|
||||
return false;
|
||||
|
||||
@ -4,15 +4,8 @@
|
||||
|
||||
using BreCalClient.misc.Model;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Controls;
|
||||
using Xceed.Wpf.Toolkit;
|
||||
using static BreCalClient.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace BreCalClient
|
||||
{
|
||||
@ -107,17 +100,14 @@ namespace BreCalClient
|
||||
{
|
||||
message = "";
|
||||
|
||||
if (this.datePickerETD.Value.HasValue && (this.datePickerETD.Value.Value < DateTime.Now) && (this.datePickerETD_End.Value == null))
|
||||
if((this.datePickerETD.Value != this.Times.EtdBerth) || (this.datePickerETD_End.Value != this.Times.EtdIntervalEnd))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETD_End.Value.HasValue && this.datePickerETD_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
if (datePickerETD.Value.IsTooOld() || datePickerETD_End.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerETD.Value.HasValue && this.datePickerETD_End.Value.HasValue && this.datePickerETD.Value > this.datePickerETD_End.Value)
|
||||
{
|
||||
@ -125,17 +115,14 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && (this.datePickerTidalWindowFrom.Value.Value < DateTime.Now) && (this.datePickerTidalWindowTo.Value == null))
|
||||
if((this.datePickerTidalWindowFrom.Value != this.ShipcallModel.Shipcall?.TidalWindowFrom) || (this.datePickerTidalWindowTo.Value != this.ShipcallModel.Shipcall?.TidalWindowTo))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowTo.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
if (this.datePickerTidalWindowTo.Value.IsTooOld() || this.datePickerTidalWindowFrom.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowFrom.Value > this.datePickerTidalWindowTo.Value)
|
||||
{
|
||||
@ -155,8 +142,7 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if((this.datePickerETD_End.Value.HasValue && !this.datePickerETD.Value.HasValue) ||
|
||||
(this.datePickerTidalWindowTo.Value.HasValue && !this.datePickerTidalWindowFrom.Value.HasValue))
|
||||
if((this.datePickerETD_End.Value.HasValue && !this.datePickerETD.Value.HasValue) || (this.datePickerTidalWindowTo.Value.HasValue && !this.datePickerTidalWindowFrom.Value.HasValue))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textStartTimeMissing;
|
||||
return false;
|
||||
|
||||
@ -97,17 +97,15 @@ namespace BreCalClient
|
||||
{
|
||||
message = "";
|
||||
|
||||
if (this.datePickerETA.Value.HasValue && (this.datePickerETA.Value.Value < DateTime.Now) && (this.datePickerETA_End.Value == null))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETA_End.Value.HasValue && this.datePickerETA_End.Value < DateTime.Now)
|
||||
if((this.datePickerETA.Value != this.Times.EtaBerth) || (this.datePickerETA_End.Value != this.Times.EtaIntervalEnd))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
if (this.datePickerETA.Value.IsTooOld() && this.datePickerETA_End.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerETA.Value.HasValue && this.datePickerETA_End.Value.HasValue && this.datePickerETA.Value > this.datePickerETA_End.Value)
|
||||
{
|
||||
@ -115,17 +113,14 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETD.Value.HasValue && (this.datePickerETD.Value.Value < DateTime.Now) && (this.datePickerETD_End.Value == null))
|
||||
if((this.datePickerETD.Value != this.Times.EtdBerth) || (this.datePickerETD_End.Value != this.Times.EtdIntervalEnd))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETD_End.Value.HasValue && this.datePickerETD_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
if (this.datePickerETD.Value.IsTooOld() || this.datePickerETD_End.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerETD.Value.HasValue && this.datePickerETD_End.Value.HasValue && this.datePickerETD.Value > this.datePickerETD_End.Value)
|
||||
{
|
||||
@ -133,17 +128,14 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && (this.datePickerTidalWindowFrom.Value.Value < DateTime.Now) && (this.datePickerTidalWindowTo.Value == null))
|
||||
if((this.datePickerTidalWindowFrom.Value != this.ShipcallModel.Shipcall?.TidalWindowFrom) || (this.datePickerTidalWindowTo.Value != this.ShipcallModel.Shipcall?.TidalWindowTo))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowTo.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
if (this.datePickerTidalWindowFrom.Value.IsTooOld() && this.datePickerTidalWindowTo.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textTideTimesInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerTidalWindowFrom.Value.HasValue && this.datePickerTidalWindowTo.Value.HasValue && this.datePickerTidalWindowFrom.Value > this.datePickerTidalWindowTo.Value)
|
||||
{
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
// Copyright (c) 2023 schick Informatik
|
||||
// Description: Single dialog to edit times for all participant types
|
||||
// (we might use different controls at a later time)
|
||||
//
|
||||
//
|
||||
|
||||
using BreCalClient.misc.Model;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Xceed.Wpf.Toolkit;
|
||||
@ -40,7 +39,7 @@ namespace BreCalClient
|
||||
#region event handler
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
{
|
||||
this.EnableControls();
|
||||
this.CopyToControls();
|
||||
}
|
||||
@ -49,7 +48,7 @@ namespace BreCalClient
|
||||
{
|
||||
if (!CheckValues(out string message))
|
||||
{
|
||||
System.Windows.MessageBox.Show(message, BreCalClient.Resources.Resources.textWarning,
|
||||
System.Windows.MessageBox.Show(message, BreCalClient.Resources.Resources.textWarning,
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
else
|
||||
@ -58,7 +57,7 @@ namespace BreCalClient
|
||||
this.DialogResult = true;
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonCancel_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
@ -99,16 +98,13 @@ namespace BreCalClient
|
||||
|
||||
message = "";
|
||||
|
||||
if (this.datePickerETABerth.Value.HasValue && (this.datePickerETABerth.Value.Value < DateTime.Now) && (this.datePickerETABerth_End.Value == null) && (this.datePickerETABerth.Value != this.Times.EtaBerth))
|
||||
if ((this.datePickerETABerth.Value != this.Times.EtaBerth) || (this.datePickerETABerth_End.Value != this.Times.EtaIntervalEnd))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETABerth_End.Value.HasValue && this.datePickerETABerth_End.Value < DateTime.Now && this.datePickerETABerth_End.Value != this.Times.EtaIntervalEnd)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
if (this.datePickerETABerth.Value.IsTooOld() || this.datePickerETABerth_End.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETAInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerETABerth.Value.HasValue && this.datePickerETABerth_End.Value.HasValue && this.datePickerETABerth.Value > this.datePickerETABerth_End.Value)
|
||||
@ -117,16 +113,13 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETDBerth.Value.HasValue && (this.datePickerETDBerth.Value.Value < DateTime.Now) && (this.datePickerETABerth_End.Value == null) && (this.datePickerETDBerth.Value != this.Times.EtdBerth))
|
||||
if((this.datePickerETDBerth.Value != this.Times.EtdBerth) || (this.datePickerETDBerth_End.Value != this.Times.EtdIntervalEnd))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerETDBerth_End.Value.HasValue && this.datePickerETDBerth_End.Value < DateTime.Now && this.datePickerETDBerth_End.Value != this.Times.EtdIntervalEnd)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
if(this.datePickerETDBerth.Value.IsTooOld() || this.datePickerETDBerth_End.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textETDInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerETDBerth.Value.HasValue && this.datePickerETDBerth_End.Value.HasValue && this.datePickerETDBerth.Value > this.datePickerETDBerth_End.Value)
|
||||
@ -135,13 +128,13 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerLockTime.Value.HasValue && (this.datePickerLockTime.Value.Value < DateTime.Now) && (this.datePickerLockTime.Value != this.Times.LockTime))
|
||||
if (this.datePickerLockTime.Value.IsTooOld() && (this.datePickerLockTime.Value != this.Times.LockTime))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textLockTimeInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerZoneEntry.Value.HasValue && this.datePickerZoneEntry.Value < DateTime.Now && this.datePickerZoneEntry.Value != this.Times.ZoneEntry)
|
||||
if (this.datePickerZoneEntry.Value.IsTooOld() && (this.datePickerZoneEntry.Value != this.Times.ZoneEntry))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textZoneEntryInThePast;
|
||||
return false;
|
||||
@ -154,8 +147,7 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if((this.datePickerETABerth_End.Value.HasValue && !this.datePickerETABerth.Value.HasValue) ||
|
||||
(this.datePickerETDBerth_End.Value.HasValue && !this.datePickerETDBerth.Value.HasValue))
|
||||
if((this.datePickerETABerth_End.Value.HasValue && !this.datePickerETABerth.Value.HasValue) || (this.datePickerETDBerth_End.Value.HasValue && !this.datePickerETDBerth.Value.HasValue))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textStartTimeMissing;
|
||||
return false;
|
||||
@ -218,13 +210,13 @@ namespace BreCalClient
|
||||
{
|
||||
this.labelETA.Content = string.Format("ETA {0}", BreCalLists.TimeRefs[displayIndex]);
|
||||
this.labelETD.Content = string.Format("ETD {0}", BreCalLists.TimeRefs[displayIndex]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.labelETA.Content = BreCalClient.Resources.Resources.textETABerth;
|
||||
this.labelETD.Content = BreCalClient.Resources.Resources.textETDBerth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.SetLockButton(this.Times.EtaBerthFixed ?? false);
|
||||
}
|
||||
@ -290,11 +282,11 @@ namespace BreCalClient
|
||||
case Extensions.ParticipantType.PORT_ADMINISTRATION:
|
||||
this.datePickerLockTime.IsEnabled = true;
|
||||
break;
|
||||
case Extensions.ParticipantType.TUG:
|
||||
case Extensions.ParticipantType.PILOT:
|
||||
this.datePickerZoneEntry.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
|
||||
case Extensions.ParticipantType.TUG:
|
||||
case Extensions.ParticipantType.PILOT:
|
||||
this.datePickerZoneEntry.IsEnabled = (ShipcallModel.Shipcall?.Type == ShipcallType.Arrival);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLockButton(bool newValue)
|
||||
@ -311,7 +303,7 @@ namespace BreCalClient
|
||||
this.imageFixedOrder.Source = new BitmapImage(new Uri(@"pack://application:,,,/Resources/lock_open.png", UriKind.RelativeOrAbsolute));
|
||||
this.buttonFixedOrder.ToolTip = BreCalClient.Resources.Resources.textTooltipSetFixedOrder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -335,17 +327,17 @@ namespace BreCalClient
|
||||
private void contextMenuItemClearZoneEntry_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.datePickerZoneEntry.Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void contextMenuItemClearATA_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.datePickerATA.Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void contextMenuItemClearATD_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.datePickerATD.Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void contextMenuItemClearETA_End_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
@ -358,6 +350,6 @@ namespace BreCalClient
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,17 +108,14 @@ namespace BreCalClient
|
||||
|
||||
message = "";
|
||||
|
||||
if (this.datePickerOperationStart.Value.HasValue && (this.datePickerOperationStart.Value.Value < DateTime.Now) && (this.datePickerOperationStart_End.Value == null))
|
||||
if((this.datePickerOperationStart.Value != this.Times.OperationsStart) || (this.datePickerOperationStart_End.Value != this.Times.EtaIntervalEnd))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationStartInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerOperationStart_End.Value.HasValue && this.datePickerOperationStart_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationStartInThePast;
|
||||
return false;
|
||||
}
|
||||
if(this.datePickerOperationStart.Value.IsTooOld() || this.datePickerOperationStart_End.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationStartInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerOperationStart.Value.HasValue && this.datePickerOperationStart_End.Value.HasValue && this.datePickerOperationStart.Value > this.datePickerOperationStart_End.Value)
|
||||
{
|
||||
@ -126,17 +123,14 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerOperationEnd.Value.HasValue && (this.datePickerOperationEnd.Value.Value < DateTime.Now) && (this.datePickerOperationEnd_End.Value == null))
|
||||
if ((this.datePickerOperationEnd.Value != this.Times.OperationsEnd) || (this.datePickerOperationEnd_End.Value != this.Times.EtdIntervalEnd))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationEndInThePast;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.datePickerOperationEnd_End.Value.HasValue && this.datePickerOperationEnd_End.Value < DateTime.Now)
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationEndInThePast;
|
||||
return false;
|
||||
}
|
||||
if(this.datePickerOperationEnd.Value.IsTooOld() || this.datePickerOperationEnd_End.Value.IsTooOld())
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textOperationEndInThePast;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.datePickerOperationEnd.Value.HasValue && this.datePickerOperationEnd_End.Value.HasValue && this.datePickerOperationEnd.Value > this.datePickerOperationEnd_End.Value)
|
||||
{
|
||||
@ -150,8 +144,7 @@ namespace BreCalClient
|
||||
return false;
|
||||
}
|
||||
|
||||
if((this.datePickerOperationEnd_End.Value.HasValue && !this.datePickerOperationEnd.Value.HasValue) ||
|
||||
(this.datePickerOperationStart_End.Value.HasValue && !this.datePickerOperationStart.Value.HasValue))
|
||||
if((this.datePickerOperationEnd_End.Value.HasValue && !this.datePickerOperationEnd.Value.HasValue) || (this.datePickerOperationStart_End.Value.HasValue && !this.datePickerOperationStart.Value.HasValue))
|
||||
{
|
||||
message = BreCalClient.Resources.Resources.textStartTimeMissing;
|
||||
return false;
|
||||
|
||||
@ -69,6 +69,14 @@ namespace BreCalClient
|
||||
return datetime > DateTime.Now.AddYears(1);
|
||||
}
|
||||
|
||||
public static bool IsTooOld(this DateTime? datetime)
|
||||
{
|
||||
if (datetime == null) return false;
|
||||
{
|
||||
return datetime < DateTime.Now.AddDays(-1);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsTypeFlagSet(this Participant participant, ParticipantType flag)
|
||||
{
|
||||
return (participant.Type & (uint)flag) != 0;
|
||||
|
||||
@ -28,7 +28,7 @@ def validation_error_default_asserts(response):
|
||||
def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict):
|
||||
"""this function applies more complex validation functions to data, which is sent to a post-request of shipcalls"""
|
||||
# DEPRECATED: this function has been refactored into InputValidationShipcall (see methods for POST and PUT evaluation)
|
||||
# #TODO_refactor: this function is pretty complex. One may instead build an object, which calls the methods separately.
|
||||
# #TODO_refactor: this function is pretty complex. One may instead build an object, which calls the methods separately.
|
||||
|
||||
##### Section 1: check user_data #####
|
||||
# check, whether the user belongs to a participant, which is of type ParticipantType.BSMD
|
||||
@ -36,23 +36,23 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
|
||||
is_bsmd = check_if_user_is_bsmd_type(user_data)
|
||||
if not is_bsmd:
|
||||
raise ValidationError({"user_participant_type":f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}"})
|
||||
|
||||
|
||||
##### Section 2: check loadedModel #####
|
||||
valid_ship_id = check_if_ship_id_is_valid(ship_id=loadedModel.get("ship_id", None))
|
||||
if not valid_ship_id:
|
||||
raise ValidationError({"ship_id":f"provided an invalid ship id, which is not found in the database: {loadedModel.get('ship_id', None)}"})
|
||||
|
||||
|
||||
valid_arrival_berth_id = check_if_berth_id_is_valid(berth_id=loadedModel.get("arrival_berth_id", None))
|
||||
if not valid_arrival_berth_id:
|
||||
raise ValidationError({"arrival_berth_id":f"provided an invalid arrival berth id, which is not found in the database: {loadedModel.get('arrival_berth_id', None)}"})
|
||||
|
||||
|
||||
valid_departure_berth_id = check_if_berth_id_is_valid(berth_id=loadedModel.get("departure_berth_id", None))
|
||||
if not valid_departure_berth_id:
|
||||
raise ValidationError({"departure_berth_id":f"provided an invalid departure berth id, which is not found in the database: {loadedModel.get('departure_berth_id', None)}"})
|
||||
|
||||
|
||||
valid_participant_ids = check_if_participant_ids_are_valid(participants=loadedModel.get("participants",[]))
|
||||
if not valid_participant_ids:
|
||||
raise ValidationError({"participants":f"one of the provided participant ids is invalid. Could not find one of these in the database: {loadedModel.get('participants', None)}"})
|
||||
raise ValidationError({"participants":f"one of the provided participant ids is invalid. Could not find one of these in the database: {loadedModel.get('participants', None)}"})
|
||||
|
||||
|
||||
##### Section 3: check content #####
|
||||
@ -63,18 +63,18 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
|
||||
value = content.get(forbidden_key, None)
|
||||
if value is not None:
|
||||
raise ValidationError({"forbidden_key":f"'{forbidden_key}' may not be set on POST. Found: {value}"})
|
||||
|
||||
|
||||
voyage_str_is_invalid = check_if_string_has_special_characters(text=content.get("voyage",""))
|
||||
if voyage_str_is_invalid:
|
||||
raise ValidationError({"voyage":f"there are invalid characters in the 'voyage'-string. Please use only digits and ASCII letters. Allowed: {ascii_letters+digits}. Found: {content.get('voyage')}"})
|
||||
|
||||
raise ValidationError({"voyage":f"there are invalid characters in the 'voyage'-string. Please use only digits and ASCII letters. Allowed: {ascii_letters+digits}. Found: {content.get('voyage')}"})
|
||||
|
||||
|
||||
##### Section 4: check loadedModel & content #####
|
||||
# #TODO_refactor: these methods should be placed in separate locations
|
||||
|
||||
# existance checks in content
|
||||
# datetime checks in loadedModel (datetime.datetime objects). Dates should be in the future.
|
||||
time_now = datetime.datetime.now()
|
||||
# datetime checks in loadedModel (datetime.datetime objects). Dates should be in the future.
|
||||
time_now = datetime.datetime.now() - datetime.timedelta(days=1)
|
||||
type_ = loadedModel.get("type", int(ShipcallType.undefined))
|
||||
if int(type_)==int(ShipcallType.undefined):
|
||||
raise ValidationError({"type":f"providing 'type' is mandatory. Missing key!"})
|
||||
@ -85,7 +85,7 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
|
||||
if content.get("arrival_berth_id", None) is None:
|
||||
raise ValidationError({"arrival_berth_id":f"providing 'arrival_berth_id' is mandatory. Missing key!"})
|
||||
if not eta >= time_now:
|
||||
raise ValidationError({"eta":f"'eta' must be in the future. Incorrect datetime provided."})
|
||||
raise ValidationError({"eta":f"'eta' is too far in the past. Incorrect datetime provided."})
|
||||
elif int(type_)==int(ShipcallType.departure):
|
||||
etd = loadedModel.get("etd")
|
||||
if (content.get("etd", None) is None):
|
||||
@ -93,7 +93,7 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
|
||||
if content.get("departure_berth_id", None) is None:
|
||||
raise ValidationError({"departure_berth_id":f"providing 'departure_berth_id' is mandatory. Missing key!"})
|
||||
if not etd >= time_now:
|
||||
raise ValidationError({"etd":f"'etd' must be in the future. Incorrect datetime provided."})
|
||||
raise ValidationError({"etd":f"'etd' is too far in the past. Incorrect datetime provided."})
|
||||
elif int(type_)==int(ShipcallType.shifting):
|
||||
eta = loadedModel.get("eta")
|
||||
etd = loadedModel.get("etd")
|
||||
@ -103,20 +103,20 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
|
||||
if (content.get("arrival_berth_id", None) is None) or (content.get("departure_berth_id", None) is None):
|
||||
raise ValidationError({"arrival_berth_id_or_departure_berth_id":f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!"})
|
||||
if (not eta >= time_now) or (not etd >= time_now) or (not eta >= etd):
|
||||
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' must be in the future. Incorrect datetime provided."})
|
||||
|
||||
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' are too far in the past. Incorrect datetime provided."})
|
||||
|
||||
tidal_window_from = loadedModel.get("tidal_window_from", None)
|
||||
tidal_window_to = loadedModel.get("tidal_window_to", None)
|
||||
if tidal_window_to is not None:
|
||||
if not tidal_window_to >= time_now:
|
||||
raise ValidationError({"tidal_window_to":f"'tidal_window_to' must be in the future. Incorrect datetime provided."})
|
||||
raise ValidationError({"tidal_window_to":f"'tidal_window_to' is too far in the past. Incorrect datetime provided."})
|
||||
|
||||
if tidal_window_from is not None:
|
||||
if not tidal_window_from >= time_now:
|
||||
raise ValidationError({"tidal_window_from":f"'tidal_window_from' must be in the future. Incorrect datetime provided."})
|
||||
|
||||
raise ValidationError({"tidal_window_from":f"'tidal_window_from' is too far in the past. Incorrect datetime provided."})
|
||||
|
||||
# #TODO: assert tidal_window_from > tidal_window_to
|
||||
|
||||
|
||||
# #TODO: len of participants > 0, if agency
|
||||
# * assigned participant for agency
|
||||
return
|
||||
@ -126,21 +126,21 @@ class InputValidation():
|
||||
def __init__(self):
|
||||
self.build_supported_models_dictionary()
|
||||
return
|
||||
|
||||
|
||||
def build_supported_models_dictionary(self):
|
||||
self.supported_models = {
|
||||
Ship:ShipValidation(),
|
||||
Shipcall:ShipcallValidation(),
|
||||
Berth:BerthValidation(),
|
||||
User:UserValidation(),
|
||||
Berth:BerthValidation(),
|
||||
User:UserValidation(),
|
||||
Participant:ParticipantValidation(),
|
||||
}
|
||||
return
|
||||
|
||||
|
||||
def assert_if_not_supported(self, dataclass_object):
|
||||
assert type(dataclass_object) in self.supported_models, f"unsupported model. Found: {type(dataclass_object)}"
|
||||
return
|
||||
|
||||
|
||||
def verify(self, dataclass_object):
|
||||
self.assert_if_not_supported(dataclass_object)
|
||||
|
||||
@ -157,17 +157,17 @@ class DataclassValidation(ABC):
|
||||
"""parent class of dataclas validators, which determines the outline of every object"""
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
|
||||
def check(self, dataclass_object) -> (list, bool):
|
||||
"""
|
||||
the 'check' method provides a default style, how each dataclass object is validated. It returns a list of violations
|
||||
the 'check' method provides a default style, how each dataclass object is validated. It returns a list of violations
|
||||
and a boolean, which determines, whether the check is passed successfully
|
||||
"""
|
||||
all_rules = self.apply_all_rules(dataclass_object)
|
||||
violations = self.filter_violations(all_rules)
|
||||
input_validation_state = self.evaluate(violations)
|
||||
return (violations, input_validation_state)
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def apply_all_rules(self, dataclass_object) -> list:
|
||||
"""
|
||||
@ -176,13 +176,13 @@ class DataclassValidation(ABC):
|
||||
"""
|
||||
all_rules = [(True, 'blank_validation_rule')]
|
||||
return all_rules
|
||||
|
||||
|
||||
def filter_violations(self, all_rules):
|
||||
"""input: all_rules, a list of tuples, where each element is (output, validation_name), which are (bool, str). """
|
||||
# if output is False, a violation is observed
|
||||
violations = [result[1] for result in all_rules if not result[0]]
|
||||
return violations
|
||||
|
||||
|
||||
def evaluate(self, violations) -> bool:
|
||||
input_validation_state = len(violations)==0
|
||||
return input_validation_state
|
||||
@ -194,7 +194,7 @@ class ShipcallValidation(DataclassValidation):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
return
|
||||
|
||||
|
||||
def apply_all_rules(self, dataclass_object) -> list:
|
||||
"""apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)"""
|
||||
raise NotImplementedError()
|
||||
@ -202,16 +202,16 @@ class ShipcallValidation(DataclassValidation):
|
||||
|
||||
|
||||
from BreCal.validators.schema_validation import ship_bollard_pull_is_defined_or_is_not_tug, ship_bollard_pull_is_none_or_in_range, ship_callsign_len_is_seven_at_maximum, ship_eni_len_is_eight, ship_imo_len_is_seven, ship_length_in_range, ship_participant_id_is_defined_or_is_not_tug, ship_participant_id_is_none_or_int, ship_width_in_range
|
||||
# skip: ship_max_draft_is_defined_or_is_not_tug, ship_max_draft_is_none_or_in_range,
|
||||
# skip: ship_max_draft_is_defined_or_is_not_tug, ship_max_draft_is_none_or_in_range,
|
||||
class ShipValidation(DataclassValidation):
|
||||
"""an object that validates a Ship dataclass object"""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
return
|
||||
|
||||
|
||||
def apply_all_rules(self, dataclass_object) -> list:
|
||||
"""apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)"""
|
||||
# skip: ship_max_draft_is_defined_or_is_not_tug, ship_max_draft_is_none_or_in_range,
|
||||
# skip: ship_max_draft_is_defined_or_is_not_tug, ship_max_draft_is_none_or_in_range,
|
||||
"""
|
||||
#TODO_ship_max_draft
|
||||
with pytest.raises(AttributeError, match="'Ship' object has no attribute 'max_draft'"):
|
||||
@ -225,14 +225,14 @@ class ShipValidation(DataclassValidation):
|
||||
check_rule(dataclass_object)
|
||||
|
||||
for check_rule in [
|
||||
ship_bollard_pull_is_defined_or_is_not_tug,
|
||||
ship_bollard_pull_is_none_or_in_range,
|
||||
ship_callsign_len_is_seven_at_maximum,
|
||||
ship_eni_len_is_eight,
|
||||
ship_imo_len_is_seven,
|
||||
ship_length_in_range,
|
||||
ship_participant_id_is_defined_or_is_not_tug,
|
||||
ship_participant_id_is_none_or_int,
|
||||
ship_bollard_pull_is_defined_or_is_not_tug,
|
||||
ship_bollard_pull_is_none_or_in_range,
|
||||
ship_callsign_len_is_seven_at_maximum,
|
||||
ship_eni_len_is_eight,
|
||||
ship_imo_len_is_seven,
|
||||
ship_length_in_range,
|
||||
ship_participant_id_is_defined_or_is_not_tug,
|
||||
ship_participant_id_is_none_or_int,
|
||||
ship_width_in_range
|
||||
]
|
||||
]
|
||||
@ -243,7 +243,7 @@ class BerthValidation(DataclassValidation):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
return
|
||||
|
||||
|
||||
def apply_all_rules(self, dataclass_object) -> list:
|
||||
"""apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)"""
|
||||
raise NotImplementedError()
|
||||
@ -254,7 +254,7 @@ class UserValidation(DataclassValidation):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
return
|
||||
|
||||
|
||||
def apply_all_rules(self, dataclass_object) -> list:
|
||||
"""apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)"""
|
||||
raise NotImplementedError()
|
||||
@ -266,7 +266,7 @@ class ParticipantValidation(DataclassValidation):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
return
|
||||
|
||||
|
||||
def apply_all_rules(self, dataclass_object) -> list:
|
||||
"""apply all input validation rules to determine, whether there are violations. returns a list of tuples (output, validation_name)"""
|
||||
|
||||
|
||||
@ -124,11 +124,14 @@ class InputValidationShipcall():
|
||||
if check_if_int_is_valid_flag(flags_value, enum_object=ParticipantFlag):
|
||||
raise ValidationError({"flags":f"incorrect value provided for 'flags'. Must be a valid combination of the flags."})
|
||||
|
||||
existing_shipcall = None
|
||||
if is_put_data:
|
||||
|
||||
existing_shipcall = InputValidationShipcall.get_shipcall_by_id(loadedModel.get("id"))
|
||||
# the type of a shipcall may not be changed. It can only be set with the initial POST-request.
|
||||
InputValidationShipcall.check_shipcall_type_is_unchanged(loadedModel)
|
||||
|
||||
InputValidationShipcall.check_times_are_in_future(loadedModel, content)
|
||||
InputValidationShipcall.check_shipcall_type_is_unchanged(loadedModel, existing_shipcall)
|
||||
|
||||
InputValidationShipcall.check_times_are_in_future(loadedModel, content, existing_shipcall)
|
||||
|
||||
# some arguments must not be provided
|
||||
InputValidationShipcall.check_forbidden_arguments(content, forbidden_keys=forbidden_keys)
|
||||
@ -255,15 +258,17 @@ class InputValidationShipcall():
|
||||
raise ValidationError({"participants":f"every participant id and type should be listed only once. Found multiple entries for one of the participants."})
|
||||
|
||||
@staticmethod
|
||||
def check_shipcall_type_is_unchanged(loadedModel:dict):
|
||||
# the type of a shipcall may only be set on POST requests. Afterwards, shipcall types may not be changed.
|
||||
query = SQLQuery.get_shipcall_by_id()
|
||||
shipcall = execute_sql_query_standalone(query=query, model=Shipcall, param={"id":loadedModel.get("id")}, command_type="single")
|
||||
|
||||
if int(loadedModel["type"]) != int(shipcall.type):
|
||||
def check_shipcall_type_is_unchanged(loadedModel:dict, existing_shipcall:object):
|
||||
if int(loadedModel["type"]) != int(existing_shipcall.type):
|
||||
raise ValidationError({"type":f"The shipcall type may only be set in the initial POST-request. Afterwards, changing the shipcall type is not allowed."}) # @pytest.raises
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def get_shipcall_by_id(shipcall_id:int):
|
||||
query = SQLQuery.get_shipcall_by_id()
|
||||
shipcall = execute_sql_query_standalone(query=query, model=Shipcall, param={"id":shipcall_id}, command_type="single")
|
||||
return shipcall
|
||||
|
||||
@staticmethod
|
||||
def check_forbidden_arguments(content:dict, forbidden_keys=["evaluation", "evaluation_message"]):
|
||||
"""
|
||||
@ -324,13 +329,13 @@ class InputValidationShipcall():
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_times_are_in_future(loadedModel:dict, content:dict):
|
||||
def check_times_are_in_future(loadedModel:dict, content:dict, existing_shipcall:object):
|
||||
"""
|
||||
Dates should be in the future. Depending on the ShipcallType, specific values should be checked
|
||||
Perfornms datetime checks in the loadedModel (datetime.datetime objects).
|
||||
Performs datetime checks in the loadedModel (datetime.datetime objects).
|
||||
"""
|
||||
# obtain the current datetime to check, whether the provided values are in the future
|
||||
time_now = datetime.datetime.now()
|
||||
# obtain the current datetime to check, whether the provided values are after ref time
|
||||
time_ref = datetime.datetime.now() - datetime.timedelta(days=1)
|
||||
|
||||
type_ = loadedModel.get("type", ShipcallType.undefined.name)
|
||||
if isinstance(type_, str): # convert the name string to a ShipcallType data model
|
||||
@ -346,16 +351,29 @@ class InputValidationShipcall():
|
||||
etd = loadedModel.get("etd")
|
||||
tidal_window_from = loadedModel.get("tidal_window_from", None)
|
||||
tidal_window_to = loadedModel.get("tidal_window_to", None)
|
||||
existing_eta = None
|
||||
existing_etd = None
|
||||
existing_tidal_window_from = None
|
||||
existing_tidal_window_to = None
|
||||
|
||||
# Estimated arrival or departure times
|
||||
InputValidationShipcall.check_times_in_future_based_on_type(type_, time_now, eta, etd)
|
||||
if existing_shipcall is not None:
|
||||
existing_eta = existing_shipcall.eta
|
||||
existing_etd = existing_shipcall.etd
|
||||
existing_tidal_window_from = existing_shipcall.tidal_window_from
|
||||
existing_tidal_window_to = existing_shipcall.tidal_window_to
|
||||
|
||||
if eta != existing_eta or etd != existing_etd:
|
||||
# Estimated arrival or departure times
|
||||
InputValidationShipcall.check_times_in_future_based_on_type(type_, time_ref, eta, etd)
|
||||
|
||||
if tidal_window_from != existing_tidal_window_from or tidal_window_to != existing_tidal_window_to:
|
||||
# Tidal Window
|
||||
InputValidationShipcall.check_tidal_window_in_future(type_, time_ref, tidal_window_from, tidal_window_to)
|
||||
|
||||
# Tidal Window
|
||||
InputValidationShipcall.check_tidal_window_in_future(type_, time_now, tidal_window_from, tidal_window_to)
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_times_in_future_based_on_type(type_, time_now, eta, etd):
|
||||
def check_times_in_future_based_on_type(type_, time_ref, eta, etd):
|
||||
"""
|
||||
checks, whether the ETA & ETD times are in the future.
|
||||
based on the type, this function checks:
|
||||
@ -365,8 +383,8 @@ class InputValidationShipcall():
|
||||
"""
|
||||
if (eta is None) and (etd is None):
|
||||
return
|
||||
|
||||
time_in_a_year = time_now.replace(time_now.year + 1)
|
||||
|
||||
time_in_a_year = datetime.datetime.now().replace(datetime.datetime.now().year + 1)
|
||||
|
||||
if type_ is None:
|
||||
raise ValidationError({"type":f"when providing 'eta' or 'etd', one must provide the type of the shipcall, so the datetimes can be verified."})
|
||||
@ -381,8 +399,8 @@ class InputValidationShipcall():
|
||||
if eta is None: # null values -> no violation
|
||||
return
|
||||
|
||||
if not eta > time_now:
|
||||
raise ValidationError({"eta":f"'eta' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETA: {eta}."})
|
||||
if not eta > time_ref:
|
||||
raise ValidationError({"eta":f"'eta' is too far in the past. Incorrect datetime provided. Current Time: {time_ref}. ETA: {eta}."})
|
||||
if etd is not None:
|
||||
raise ValidationError({"etd":f"'etd' should not be set when the shipcall type is 'arrival'."})
|
||||
if eta > time_in_a_year:
|
||||
@ -392,8 +410,9 @@ class InputValidationShipcall():
|
||||
if etd is None: # null values -> no violation
|
||||
return
|
||||
|
||||
if not etd > time_now:
|
||||
raise ValidationError({"etd":f"'etd' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETD: {etd}."})
|
||||
if not etd > time_ref:
|
||||
raise ValidationError({"etd":f"'etd' is too far in the past. Incorrect datetime provided. Current Time: {time_ref}. ETD: {etd}."})
|
||||
|
||||
if eta is not None:
|
||||
raise ValidationError({"eta":f"'eta' should not be set when the shipcall type is 'departure'."})
|
||||
if etd > time_in_a_year:
|
||||
@ -408,8 +427,8 @@ class InputValidationShipcall():
|
||||
# rules, a user is only allowed to provide *both* values.
|
||||
raise ValidationError({"eta_or_etd":f"For shifting shipcalls one should always provide, both, eta and etd."})
|
||||
|
||||
if (not eta > time_now) or (not etd > time_now):
|
||||
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETA: {eta}. ETD: {etd}"})
|
||||
if (not eta > time_ref) or (not etd > time_ref):
|
||||
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' is too far in the past. Incorrect datetime provided. Current Time: {time_ref}. ETA: {eta}. ETD: {etd}"})
|
||||
if (not etd < eta):
|
||||
raise ValidationError({"eta_or_etd":f"The estimated time of departure ('etd') must take place *before the estimated time of arrival ('eta'). The ship cannot arrive, before it has departed. Found: ETD: {etd}, ETA: {eta}"})
|
||||
|
||||
@ -419,30 +438,29 @@ class InputValidationShipcall():
|
||||
raise ValidationError({"eta":f"'eta' is more than a year in the future. ETA: {eta}."})
|
||||
if etd > time_in_a_year:
|
||||
raise ValidationError({"etd":f"'etd' is more than a year in the future. ETD: {etd}."})
|
||||
|
||||
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def check_tidal_window_in_future(type_, time_now, tidal_window_from, tidal_window_to):
|
||||
|
||||
time_in_a_year = time_now.replace(time_now.year + 1)
|
||||
def check_tidal_window_in_future(type_, time_ref, tidal_window_from, tidal_window_to):
|
||||
|
||||
time_in_a_year = datetime.datetime.now().replace(datetime.datetime.now().year + 1)
|
||||
if tidal_window_to is not None:
|
||||
if not tidal_window_to >= time_now:
|
||||
raise ValidationError({"tidal_window_to":f"'tidal_window_to' must be in the future. Incorrect datetime provided."})
|
||||
if not tidal_window_to >= time_ref:
|
||||
raise ValidationError({"tidal_window_to":f"'tidal_window_to' is too far in the past. Incorrect datetime provided."})
|
||||
if tidal_window_to > time_in_a_year:
|
||||
raise ValidationError({"tidal_window_to":f"'tidal_window_to' is more than a year in the future. Found: {tidal_window_to}."})
|
||||
|
||||
if tidal_window_from is not None:
|
||||
if not tidal_window_from >= time_now:
|
||||
raise ValidationError({"tidal_window_from":f"'tidal_window_from' must be in the future. Incorrect datetime provided."})
|
||||
if not tidal_window_from >= time_ref:
|
||||
raise ValidationError({"tidal_window_from":f"'tidal_window_from' is too far in the past. Incorrect datetime provided."})
|
||||
if tidal_window_from > time_in_a_year:
|
||||
raise ValidationError({"tidal_window_from":f"'tidal_window_from' is more than a year in the future. Found: {tidal_window_from}."})
|
||||
|
||||
if (tidal_window_to is not None) and (tidal_window_from is not None):
|
||||
if tidal_window_to < tidal_window_from:
|
||||
raise ValidationError({"tidal_window_to_or_tidal_window_from":f"'tidal_window_to' must take place after 'tidal_window_from'. Incorrect datetime provided. Found 'tidal_window_to': {tidal_window_to}, 'tidal_window_from': {tidal_window_to}."})
|
||||
|
||||
|
||||
if (tidal_window_to is not None and tidal_window_from is None) or (tidal_window_to is None and tidal_window_from is not None):
|
||||
raise ValidationError({"tidal_window_to_or_tidal_window_from":f"'tidal_window_to' and 'tidal_window_from' must both be provided."})
|
||||
|
||||
|
||||
@ -414,6 +414,8 @@ class InputValidationTimes():
|
||||
# commonly used in the PUT-request
|
||||
if loadedModel is not None:
|
||||
(shipcall_id, times_assigned_participant) = InputValidationTimes.prepare_authority_check_for_put_request(loadedModel)
|
||||
else:
|
||||
loadedModel = get_times_data_for_id(times_id)
|
||||
|
||||
# commonly used in the DELETE-request
|
||||
if times_id is not None:
|
||||
|
||||
@ -207,7 +207,7 @@ def test_shipcall_post_request_fails_when_voyage_string_is_invalid(get_stub_toke
|
||||
with pytest.raises(ValidationError, match="Longer than maximum length 16"):
|
||||
assert response.status_code==400
|
||||
raise ValidationError(response.json())
|
||||
|
||||
|
||||
# Fail: special characters
|
||||
post_data = original_post_data.copy()
|
||||
post_data["voyage"] = '👽'
|
||||
@ -226,17 +226,17 @@ def test_shipcall_post_request_fails_when_type_arrival_and_not_in_future(get_stu
|
||||
# accept
|
||||
post_data = original_post_data.copy()
|
||||
post_data["type"] = ShipcallType.arrival.name
|
||||
post_data["eta"] = (datetime.datetime.now() + datetime.timedelta(hours=3)).isoformat()
|
||||
post_data["eta"] = (datetime.datetime.now() + datetime.timedelta(days=2)).isoformat()
|
||||
response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data)
|
||||
assert response.status_code == 201
|
||||
|
||||
# error
|
||||
post_data = original_post_data.copy()
|
||||
post_data["type"] = ShipcallType.arrival.name
|
||||
post_data["eta"] = (datetime.datetime.now() - datetime.timedelta(hours=3)).isoformat()
|
||||
post_data["eta"] = (datetime.datetime.now() - datetime.timedelta(days=2)).isoformat()
|
||||
response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data)
|
||||
|
||||
with pytest.raises(ValidationError, match="must be in the future. Incorrect datetime provided"):
|
||||
with pytest.raises(ValidationError, match="is too far in the past. Incorrect datetime provided"):
|
||||
assert response.status_code==400
|
||||
raise ValidationError(response.json())
|
||||
return
|
||||
@ -256,10 +256,10 @@ def test_shipcall_post_request_fails_when_type_departure_and_not_in_future(get_s
|
||||
# error
|
||||
post_data = original_post_data.copy()
|
||||
post_data["type"] = ShipcallType.departure.name
|
||||
post_data["etd"] = (datetime.datetime.now() - datetime.timedelta(hours=3)).isoformat()
|
||||
post_data["etd"] = (datetime.datetime.now() - datetime.timedelta(days=3)).isoformat()
|
||||
response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data)
|
||||
|
||||
with pytest.raises(ValidationError, match="must be in the future. Incorrect datetime provided"):
|
||||
with pytest.raises(ValidationError, match="is too far in the past. Incorrect datetime provided"):
|
||||
assert response.status_code==400
|
||||
raise ValidationError(response.json())
|
||||
return
|
||||
@ -280,11 +280,11 @@ def test_shipcall_post_request_fails_when_type_shifting_and_not_in_future(get_st
|
||||
# error
|
||||
post_data = original_post_data.copy()
|
||||
post_data["type"] = ShipcallType.shifting.name
|
||||
post_data["etd"] = (datetime.datetime.now() - datetime.timedelta(hours=3)).isoformat()
|
||||
post_data["etd"] = (datetime.datetime.now() - datetime.timedelta(days=3)).isoformat()
|
||||
post_data["eta"] = (datetime.datetime.now() + datetime.timedelta(hours=3,minutes=1)).isoformat()
|
||||
response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data)
|
||||
|
||||
with pytest.raises(ValidationError, match="must be in the future. Incorrect datetime provided"):
|
||||
with pytest.raises(ValidationError, match="is too far in the past. Incorrect datetime provided"):
|
||||
assert response.status_code==400
|
||||
raise ValidationError(response.json())
|
||||
return
|
||||
@ -629,7 +629,7 @@ def test_shipcall_put_request_fails_when_different_participant_id_is_assigned(ge
|
||||
{"id":99115, 'participant_id': 5, 'type': 8}]
|
||||
spm_shipcall_data = [
|
||||
{**{"created":created, "modified":modified, "shipcall_id":shipcall_id}, **spm}
|
||||
for spm in
|
||||
for spm in
|
||||
spm_shipcall_data
|
||||
]
|
||||
spm_shipcall_data = [ShipcallParticipantMap(**spm) for spm in spm_shipcall_data]
|
||||
@ -652,7 +652,7 @@ def test_shipcall_put_request_success(get_shipcall_id_after_stub_post_request):
|
||||
user_data = {'id':6, 'participant_id':1}
|
||||
loadedModel = post_data
|
||||
content = post_data
|
||||
|
||||
|
||||
created = datetime.datetime.now()+datetime.timedelta(minutes=1)
|
||||
modified = datetime.datetime.now()+datetime.timedelta(minutes=2)
|
||||
|
||||
@ -662,7 +662,7 @@ def test_shipcall_put_request_success(get_shipcall_id_after_stub_post_request):
|
||||
{"id":99115, 'participant_id': 5, 'type': 8}]
|
||||
spm_shipcall_data = [
|
||||
{**{"created":created, "modified":modified, "shipcall_id":shipcall_id}, **spm}
|
||||
for spm in
|
||||
for spm in
|
||||
spm_shipcall_data
|
||||
]
|
||||
spm_shipcall_data = [ShipcallParticipantMap(**spm) for spm in spm_shipcall_data]
|
||||
@ -687,7 +687,7 @@ def test_shipcall_put_request_fails_when_no_agency_is_assigned(get_shipcall_id_a
|
||||
user_data = {'id':6, 'participant_id':2} # participant_id 2 is not BSMD and is not authorized.
|
||||
loadedModel = post_data
|
||||
content = post_data
|
||||
|
||||
|
||||
created = datetime.datetime.now()+datetime.timedelta(minutes=1)
|
||||
modified = datetime.datetime.now()+datetime.timedelta(minutes=2)
|
||||
|
||||
@ -697,7 +697,7 @@ def test_shipcall_put_request_fails_when_no_agency_is_assigned(get_shipcall_id_a
|
||||
{"id":99115, 'participant_id': 5, 'type': 4}]
|
||||
spm_shipcall_data = [
|
||||
{**{"created":created, "modified":modified, "shipcall_id":shipcall_id}, **spm}
|
||||
for spm in
|
||||
for spm in
|
||||
spm_shipcall_data
|
||||
]
|
||||
spm_shipcall_data = [ShipcallParticipantMap(**spm) for spm in spm_shipcall_data]
|
||||
@ -705,7 +705,7 @@ def test_shipcall_put_request_fails_when_no_agency_is_assigned(get_shipcall_id_a
|
||||
|
||||
# no agency assigned
|
||||
ivs = InputValidationShipcall()
|
||||
|
||||
|
||||
with pytest.raises(werkzeug.exceptions.Forbidden, match=re.escape(f"PUT Requests for shipcalls can only be issued by an assigned AGENCY or BSMD users (if the special-flag is enabled). There is no assigned agency yet, so only BSMD users can change datasets.")):
|
||||
ivs.check_user_is_authorized_for_put_request(user_data, loadedModel, content, spm_shipcall_data)
|
||||
return
|
||||
@ -730,7 +730,7 @@ def test_shipcall_put_request_fails_when_user_is_not_authorized(get_shipcall_id_
|
||||
{"id":99115, 'participant_id': 5, 'type': 4}]
|
||||
spm_shipcall_data = [
|
||||
{**{"created":created, "modified":modified, "shipcall_id":shipcall_id}, **spm}
|
||||
for spm in
|
||||
for spm in
|
||||
spm_shipcall_data
|
||||
]
|
||||
spm_shipcall_data = [ShipcallParticipantMap(**spm) for spm in spm_shipcall_data]
|
||||
@ -754,20 +754,20 @@ def test_shipcall_put_request_fails_when_user_tries_self_assignment(get_shipcall
|
||||
|
||||
created = datetime.datetime.now()+datetime.timedelta(minutes=1)
|
||||
modified = datetime.datetime.now()+datetime.timedelta(minutes=2)
|
||||
|
||||
|
||||
spm_shipcall_data = [
|
||||
{"id":99113, 'participant_id': 3, 'type': 1},
|
||||
{"id":99114, 'participant_id': 4, 'type': 2},
|
||||
{"id":99115, 'participant_id': 5, 'type': 4}]
|
||||
spm_shipcall_data = [
|
||||
{**{"created":created, "modified":modified, "shipcall_id":shipcall_id}, **spm}
|
||||
for spm in
|
||||
for spm in
|
||||
spm_shipcall_data
|
||||
]
|
||||
spm_shipcall_data = [ShipcallParticipantMap(**spm) for spm in spm_shipcall_data]
|
||||
|
||||
|
||||
# self-assignment. User is participant 6, and wants to assign participant 6.
|
||||
# self-assignment. User is participant 6, and wants to assign participant 6.
|
||||
ivs = InputValidationShipcall()
|
||||
with pytest.raises(werkzeug.exceptions.Forbidden, match=re.escape("PUT Requests for shipcalls can only be issued by an assigned AGENCY or BSMD users (if the special-flag is enabled). There is no assigned agency yet, so only BSMD users can change datasets.")):
|
||||
# previous error message: An agency cannot self-register for a shipcall. The request is issued by an agency-user and tries to assign an AGENCY as the participant of the shipcall.""
|
||||
@ -813,7 +813,7 @@ def test_shipcall_put_request_works_if_most_values_are_null():
|
||||
|
||||
InputValidationShipcall.evaluate_put_data(user_data, loadedModel, content)
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -860,10 +860,10 @@ def test_shipcall_put_request_fails_input_validation_shipcall_when_shipcall_is_c
|
||||
return
|
||||
|
||||
def test_post_data_with_valid_data(get_stub_token):
|
||||
"""This unit test uses the input data from
|
||||
"""This unit test uses the input data from
|
||||
# https://trello.com/c/VXVSLTF4/267-shipcall-anlegen-shifting-erh%C3%A4lt-fehler-aufgrund-fr%C3%BCherem-etd-als-eta
|
||||
|
||||
to make sure, the failure case no longer appears.
|
||||
to make sure, the failure case no longer appears.
|
||||
"""
|
||||
url, token = get_stub_token["url"], get_stub_token["token"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user