Merge branch 'bugfix/empty_times'
This commit is contained in:
commit
956ba2832a
@ -1 +1 @@
|
||||
0.9.6.0
|
||||
1.0.0.0
|
||||
@ -8,8 +8,8 @@
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<StartupObject>BreCalClient.App</StartupObject>
|
||||
<AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile>
|
||||
<AssemblyVersion>0.9.6.0</AssemblyVersion>
|
||||
<FileVersion>0.9.6.0</FileVersion>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<Title>Bremen calling client</Title>
|
||||
<Description>A Windows WPF client for the Bremen calling API.</Description>
|
||||
<ApplicationIcon>containership.ico</ApplicationIcon>
|
||||
|
||||
@ -65,24 +65,15 @@
|
||||
<ColumnDefinition Width=".5*" />
|
||||
<ColumnDefinition Width=".5*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Name="comboBoxArrivalBerth" Grid.Column="0" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemDepartureBerth" Click="contextMenuItemDepartureBerth_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
<ComboBox Name="comboBoxDepartureBerth" Grid.Column="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearValue}" Name="contextMenuItemArrivalBerth" Click="contextMenuItemArrivalBerth_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
<ComboBox Name="comboBoxArrivalBerth" Grid.Column="0" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxArrivalBerth_SelectionChanged"/>
|
||||
|
||||
<ComboBox Name="comboBoxDepartureBerth" Grid.Column="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxDepartureBerth_SelectionChanged"/>
|
||||
|
||||
|
||||
</Grid>
|
||||
|
||||
<xctk:DateTimePicker x:Name="datePickerETA" Grid.Column="3" Grid.Row="2" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False"/>
|
||||
<xctk:DateTimePicker x:Name="datePickerETD" Grid.Column="3" Grid.Row="3" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False"/>
|
||||
<xctk:DateTimePicker x:Name="datePickerETA" Grid.Column="3" Grid.Row="2" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False" ValueChanged="datePickerETA_ValueChanged"/>
|
||||
<xctk:DateTimePicker x:Name="datePickerETD" Grid.Column="3" Grid.Row="3" Margin="2" Format="Custom" FormatString="dd.MM. yyyy HH:mm" IsEnabled="False" ValueChanged="datePickerETD_ValueChanged"/>
|
||||
|
||||
<Label Content="{x:Static p:Resources.textAgency}" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Right"/>
|
||||
<ComboBox Name="comboBoxAgency" Grid.Column="3" Grid.Row="4" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id" SelectionChanged="comboBoxAgency_SelectionChanged">
|
||||
|
||||
@ -67,8 +67,7 @@ namespace BreCalClient
|
||||
}
|
||||
|
||||
private void comboBoxShip_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
this.buttonOK.IsEnabled = this.comboBoxShip.SelectedItem != null;
|
||||
{
|
||||
if (this.comboBoxShip.SelectedItem != null)
|
||||
{
|
||||
Ship? ship = this.comboBoxShip.SelectedItem as Ship;
|
||||
@ -84,11 +83,13 @@ namespace BreCalClient
|
||||
this.doubleUpDownLength.Value = null;
|
||||
this.doubleUpDownWidth.Value = null;
|
||||
}
|
||||
this.CheckForCompletion();
|
||||
}
|
||||
|
||||
private void comboBoxAgency_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
this.EnableControls();
|
||||
this.CheckForCompletion();
|
||||
}
|
||||
|
||||
private void comboBoxCategories_SelectionChanged(object? sender, SelectionChangedEventArgs? e)
|
||||
@ -122,6 +123,7 @@ namespace BreCalClient
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.CheckForCompletion();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -133,22 +135,24 @@ namespace BreCalClient
|
||||
this.comboBoxAgency.SelectedIndex = -1;
|
||||
this.ShipcallModel.AssignedParticipants.Remove(Extensions.ParticipantType.AGENCY);
|
||||
}
|
||||
|
||||
private void contextMenuItemArrivalBerth_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.comboBoxArrivalBerth.SelectedIndex = -1;
|
||||
this.ShipcallModel.Berth = "";
|
||||
}
|
||||
|
||||
private void contextMenuItemDepartureBerth_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.comboBoxDepartureBerth.SelectedIndex -= 1;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region private methods
|
||||
|
||||
void CheckForCompletion()
|
||||
{
|
||||
bool isEnabled = true;
|
||||
|
||||
isEnabled &= this.comboBoxShip.SelectedItem != null;
|
||||
isEnabled &= this.comboBoxCategories.SelectedItem != null;
|
||||
isEnabled &= ((this.comboBoxArrivalBerth.SelectedItem != null) || (this.comboBoxDepartureBerth.SelectedItem != null));
|
||||
isEnabled &= (this.datePickerETA.Value.HasValue || this.datePickerETD.Value.HasValue);
|
||||
isEnabled &= this.comboBoxAgency.SelectedItem != null;
|
||||
|
||||
this.buttonOK.IsEnabled = isEnabled;
|
||||
}
|
||||
|
||||
private void CopyToModel()
|
||||
{
|
||||
if (this.ShipcallModel.Shipcall != null)
|
||||
@ -293,5 +297,24 @@ namespace BreCalClient
|
||||
|
||||
#endregion
|
||||
|
||||
private void datePickerETA_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
this.CheckForCompletion();
|
||||
}
|
||||
|
||||
private void datePickerETD_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
this.CheckForCompletion();
|
||||
}
|
||||
|
||||
private void comboBoxArrivalBerth_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
this.CheckForCompletion();
|
||||
}
|
||||
|
||||
private void comboBoxDepartureBerth_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
this.CheckForCompletion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,37 +73,27 @@
|
||||
<Label Content="{x:Static p:Resources.textAnchored}" Grid.Column="2" Grid.Row="0" HorizontalContentAlignment="Right"/>
|
||||
<CheckBox x:Name="checkBoxAnchored" Grid.Column="3" Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<Label Content="{x:Static p:Resources.textTugRequired}" Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Right"/>
|
||||
<Grid Grid.Column="3" Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="28" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="checkBoxTugRequired" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<ComboBox Name="comboBoxTug" Grid.Column="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearTug" Click="contextMenuItemClearTug_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
|
||||
<ComboBox Name="comboBoxTug" Grid.Column="3" Grid.Row="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearTug" Click="contextMenuItemClearTug_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
|
||||
<Label Content="{x:Static p:Resources.textRecommendedTugs}" Grid.Column="2" Grid.Row="2" HorizontalContentAlignment="Right"/>
|
||||
<xctk:IntegerUpDown x:Name="integerUpDownRecommendedTugs" Grid.Column="3" Grid.Row="2" Minimum="0" Margin="2" />
|
||||
<Label Content="{x:Static p:Resources.textPilotRequired}" Grid.Column="2" Grid.Row="3" HorizontalContentAlignment="Right" />
|
||||
<Grid Grid.Column="3" Grid.Row="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="28" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="checkBoxPilotRequired" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<ComboBox Name="comboBoxPilot" Grid.Column="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearPilot" Click="contextMenuItemClearPilot_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
|
||||
<ComboBox Name="comboBoxPilot" Grid.Column="3" Grid.Row="3" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearPilot" Click="contextMenuItemClearPilot_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
|
||||
<Label Content="{x:Static p:Resources.textMooring}" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Right"/>
|
||||
<ComboBox Name="comboBoxMooring" Grid.Column="3" Grid.Row="4" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
|
||||
@ -103,9 +103,9 @@ namespace BreCalClient
|
||||
this.ShipcallModel.Shipcall.Canceled = this.checkBoxCanceled.IsChecked;
|
||||
|
||||
this.ShipcallModel.Shipcall.Anchored = this.checkBoxAnchored.IsChecked;
|
||||
this.ShipcallModel.Shipcall.TugRequired = this.checkBoxTugRequired.IsChecked;
|
||||
|
||||
this.ShipcallModel.Shipcall.RecommendedTugs = this.integerUpDownRecommendedTugs.Value;
|
||||
this.ShipcallModel.Shipcall.PilotRequired = this.checkBoxPilotRequired.IsChecked;
|
||||
|
||||
this.ShipcallModel.Shipcall.MooredLock = this.checkBoxMooredLock.IsChecked;
|
||||
this.ShipcallModel.Shipcall.Bunkering = this.checkBoxBunkering.IsChecked;
|
||||
this.ShipcallModel.Shipcall.ReplenishingTerminal = this.checkBoxReplenishingTerminal.IsChecked;
|
||||
@ -190,9 +190,9 @@ namespace BreCalClient
|
||||
this.checkBoxCanceled.IsChecked = this.ShipcallModel.Shipcall.Canceled ?? false;
|
||||
|
||||
this.checkBoxAnchored.IsChecked = this.ShipcallModel.Shipcall.Anchored ?? false;
|
||||
this.checkBoxTugRequired.IsChecked = this.ShipcallModel.Shipcall.TugRequired ?? false;
|
||||
|
||||
this.integerUpDownRecommendedTugs.Value = this.ShipcallModel.Shipcall.RecommendedTugs;
|
||||
this.checkBoxPilotRequired.IsChecked = this.ShipcallModel.Shipcall.PilotRequired ?? false;
|
||||
|
||||
|
||||
this.checkBoxMooredLock.IsChecked = this.ShipcallModel.Shipcall.MooredLock ?? false;
|
||||
|
||||
@ -250,10 +250,10 @@ namespace BreCalClient
|
||||
this.checkBoxCanceled.IsEnabled = isEnabled;
|
||||
|
||||
this.checkBoxAnchored.IsEnabled = isEnabled;
|
||||
this.checkBoxTugRequired.IsEnabled = isEnabled;
|
||||
|
||||
this.comboBoxTug.IsEnabled = isEnabled;
|
||||
this.integerUpDownRecommendedTugs.IsEnabled = isEnabled;
|
||||
this.checkBoxPilotRequired.IsEnabled = isEnabled;
|
||||
|
||||
this.comboBoxPilot.IsEnabled = isEnabled;
|
||||
this.comboBoxMooring.IsEnabled = isEnabled;
|
||||
this.checkBoxMooredLock.IsEnabled = isEnabled;
|
||||
|
||||
@ -67,37 +67,27 @@
|
||||
<CheckBox x:Name="checkBoxCanceled" Grid.Column="1" Grid.Row="10" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,4,0" />
|
||||
|
||||
<Label Content="{x:Static p:Resources.textTugRequired}" Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Right"/>
|
||||
<Grid Grid.Column="3" Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="28" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="checkBoxTugRequired" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<ComboBox Name="comboBoxTug" Grid.Column="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearTug" Click="contextMenuItemClearTug_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
|
||||
<ComboBox Name="comboBoxTug" Grid.Column="3" Grid.Row="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearTug" Click="contextMenuItemClearTug_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
|
||||
<Label Content="{x:Static p:Resources.textRecommendedTugs}" Grid.Column="2" Grid.Row="2" HorizontalContentAlignment="Right"/>
|
||||
<xctk:IntegerUpDown x:Name="integerUpDownRecommendedTugs" Grid.Column="3" Grid.Row="2" Minimum="0" Margin="2" />
|
||||
<Label Content="{x:Static p:Resources.textPilotRequired}" Grid.Column="2" Grid.Row="3" HorizontalContentAlignment="Right" />
|
||||
<Grid Grid.Column="3" Grid.Row="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="28" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="checkBoxPilotRequired" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<ComboBox Name="comboBoxPilot" Grid.Column="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearPilot" Click="contextMenuItemClearPilot_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
|
||||
<ComboBox Name="comboBoxPilot" Grid.Column="3" Grid.Row="3" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearPilot" Click="contextMenuItemClearPilot_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
|
||||
<Label Content="{x:Static p:Resources.textMooring}" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Right"/>
|
||||
<ComboBox Name="comboBoxMooring" Grid.Column="3" Grid.Row="4" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
|
||||
@ -100,9 +100,9 @@ namespace BreCalClient
|
||||
this.ShipcallModel.Shipcall.TidalWindowTo = this.datePickerTidalWindowTo.Value;
|
||||
this.ShipcallModel.Shipcall.Canceled = this.checkBoxCanceled.IsChecked;
|
||||
|
||||
this.ShipcallModel.Shipcall.TugRequired = this.checkBoxTugRequired.IsChecked;
|
||||
|
||||
this.ShipcallModel.Shipcall.RecommendedTugs = this.integerUpDownRecommendedTugs.Value;
|
||||
this.ShipcallModel.Shipcall.PilotRequired = this.checkBoxPilotRequired.IsChecked;
|
||||
|
||||
this.ShipcallModel.Shipcall.MooredLock = this.checkBoxMooredLock.IsChecked;
|
||||
this.ShipcallModel.Shipcall.RainSensitiveCargo = this.checkBoxRainsensitiveCargo.IsChecked;
|
||||
if(!string.IsNullOrEmpty(this.textBoxRemarks.Text.Trim()))
|
||||
@ -183,9 +183,9 @@ namespace BreCalClient
|
||||
this.datePickerTidalWindowTo.Value = this.ShipcallModel.Shipcall.TidalWindowTo;
|
||||
this.checkBoxCanceled.IsChecked = this.ShipcallModel.Shipcall.Canceled ?? false;
|
||||
|
||||
this.checkBoxTugRequired.IsChecked = this.ShipcallModel.Shipcall.TugRequired ?? false;
|
||||
|
||||
this.integerUpDownRecommendedTugs.Value = this.ShipcallModel.Shipcall.RecommendedTugs;
|
||||
this.checkBoxPilotRequired.IsChecked = this.ShipcallModel.Shipcall.PilotRequired ?? false;
|
||||
|
||||
|
||||
this.checkBoxMooredLock.IsChecked = this.ShipcallModel.Shipcall.MooredLock ?? false;
|
||||
this.checkBoxRainsensitiveCargo.IsChecked = this.ShipcallModel.Shipcall.RainSensitiveCargo ?? false;
|
||||
@ -238,10 +238,9 @@ namespace BreCalClient
|
||||
this.datePickerTidalWindowTo.IsEnabled = isEnabled;
|
||||
this.checkBoxCanceled.IsEnabled = isEnabled;
|
||||
|
||||
this.checkBoxTugRequired.IsEnabled = isEnabled;
|
||||
|
||||
this.comboBoxTug.IsEnabled = isEnabled;
|
||||
this.integerUpDownRecommendedTugs.IsEnabled = isEnabled;
|
||||
this.checkBoxPilotRequired.IsEnabled = isEnabled;
|
||||
this.integerUpDownRecommendedTugs.IsEnabled = isEnabled;
|
||||
this.comboBoxPilot.IsEnabled = isEnabled;
|
||||
this.comboBoxMooring.IsEnabled = isEnabled;
|
||||
this.checkBoxMooredLock.IsEnabled = isEnabled;
|
||||
|
||||
@ -103,37 +103,27 @@
|
||||
|
||||
|
||||
<Label Content="{x:Static p:Resources.textTugRequired}" Grid.Column="2" Grid.Row="1" HorizontalContentAlignment="Right"/>
|
||||
<Grid Grid.Column="3" Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="28" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="checkBoxTugRequired" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<ComboBox Name="comboBoxTug" Grid.Column="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearTug" Click="contextMenuItemClearTug_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
|
||||
<ComboBox Name="comboBoxTug" Grid.Column="3" Grid.Row="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearTug" Click="contextMenuItemClearTug_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
|
||||
<Label Content="{x:Static p:Resources.textRecommendedTugs}" Grid.Column="2" Grid.Row="2" HorizontalContentAlignment="Right"/>
|
||||
<xctk:IntegerUpDown x:Name="integerUpDownRecommendedTugs" Grid.Column="3" Grid.Row="2" Minimum="0" Margin="2" />
|
||||
<Label Content="{x:Static p:Resources.textPilotRequired}" Grid.Column="2" Grid.Row="3" HorizontalContentAlignment="Right" />
|
||||
<Grid Grid.Column="3" Grid.Row="3">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="28" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="checkBoxPilotRequired" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" />
|
||||
<ComboBox Name="comboBoxPilot" Grid.Column="1" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearPilot" Click="contextMenuItemClearPilot_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
|
||||
<ComboBox Name="comboBoxPilot" Grid.Column="3" Grid.Row="3" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static p:Resources.textClearAssignment}" Name="contextMenuItemClearPilot" Click="contextMenuItemClearPilot_Click" />
|
||||
</ContextMenu>
|
||||
</ComboBox.ContextMenu>
|
||||
</ComboBox>
|
||||
|
||||
<Label Content="{x:Static p:Resources.textMooring}" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Right"/>
|
||||
<ComboBox Name="comboBoxMooring" Grid.Column="3" Grid.Row="4" Margin="2" DisplayMemberPath="Name" SelectedValuePath="Id">
|
||||
<ComboBox.ContextMenu>
|
||||
|
||||
@ -104,9 +104,9 @@ namespace BreCalClient
|
||||
this.ShipcallModel.Shipcall.Canceled = this.checkBoxCanceled.IsChecked;
|
||||
|
||||
|
||||
this.ShipcallModel.Shipcall.TugRequired = this.checkBoxTugRequired.IsChecked;
|
||||
|
||||
this.ShipcallModel.Shipcall.RecommendedTugs = this.integerUpDownRecommendedTugs.Value;
|
||||
this.ShipcallModel.Shipcall.PilotRequired = this.checkBoxPilotRequired.IsChecked;
|
||||
|
||||
this.ShipcallModel.Shipcall.MooredLock = this.checkBoxMooredLock.IsChecked;
|
||||
this.ShipcallModel.Shipcall.RainSensitiveCargo = this.checkBoxRainsensitiveCargo.IsChecked;
|
||||
if(!string.IsNullOrEmpty(this.textBoxRemarks.Text.Trim()))
|
||||
@ -197,11 +197,10 @@ namespace BreCalClient
|
||||
this.doubleUpDownDraft.Value = this.ShipcallModel.Shipcall.Draft;
|
||||
this.datePickerTidalWindowFrom.Value = this.ShipcallModel.Shipcall.TidalWindowFrom;
|
||||
this.datePickerTidalWindowTo.Value = this.ShipcallModel.Shipcall.TidalWindowTo;
|
||||
this.checkBoxCanceled.IsChecked = this.ShipcallModel.Shipcall.Canceled ?? false;
|
||||
this.checkBoxCanceled.IsChecked = this.ShipcallModel.Shipcall.Canceled ?? false;
|
||||
|
||||
this.checkBoxTugRequired.IsChecked = this.ShipcallModel.Shipcall.TugRequired ?? false;
|
||||
this.integerUpDownRecommendedTugs.Value = this.ShipcallModel.Shipcall.RecommendedTugs;
|
||||
this.checkBoxPilotRequired.IsChecked = this.ShipcallModel.Shipcall.PilotRequired ?? false;
|
||||
|
||||
|
||||
this.checkBoxMooredLock.IsChecked = this.ShipcallModel.Shipcall.MooredLock ?? false;
|
||||
this.checkBoxRainsensitiveCargo.IsChecked = this.ShipcallModel.Shipcall.RainSensitiveCargo ?? false;
|
||||
@ -255,10 +254,9 @@ namespace BreCalClient
|
||||
this.textBoxBerthRemarksArrival.IsEnabled = isEnabled;
|
||||
this.checkBoxCanceled.IsEnabled = isEnabled;
|
||||
|
||||
this.checkBoxTugRequired.IsEnabled = isEnabled;
|
||||
|
||||
this.comboBoxTug.IsEnabled = isEnabled;
|
||||
this.integerUpDownRecommendedTugs.IsEnabled = isEnabled;
|
||||
this.checkBoxPilotRequired.IsEnabled = isEnabled;
|
||||
this.integerUpDownRecommendedTugs.IsEnabled = isEnabled;
|
||||
this.comboBoxPilot.IsEnabled = isEnabled;
|
||||
this.comboBoxMooring.IsEnabled = isEnabled;
|
||||
this.checkBoxMooredLock.IsEnabled = isEnabled;
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<Label Content="{x:Static p:Resources.textPassword}" Grid.Row="2" VerticalContentAlignment="Center" />
|
||||
<TextBox Name="textUsername" Grid.Row="1" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" />
|
||||
<PasswordBox Name="textPassword" Grid.Row="2" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" PasswordChar="*"/>
|
||||
<Label Name="labelLoginResult" Grid.Row="3" Grid.ColumnSpan="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
|
||||
<Label Name="labelLoginResult" Grid.Row="3" Grid.ColumnSpan="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontWeight="Bold" />
|
||||
<Button Name="buttonLogin" Content="{x:Static p:Resources.textLogin}" Grid.Row="4" Grid.Column="0" Margin="2" Click="buttonLogin_Click" IsDefault="True" />
|
||||
<Button Name="buttonExit" Content="{x:Static p:Resources.textExit}" Grid.Row="4" Grid.Column="1" Margin="2" Click="buttonExit_Click" />
|
||||
</Grid>
|
||||
|
||||
@ -17,6 +17,8 @@ using BreCalClient.misc.Model;
|
||||
|
||||
using static BreCalClient.Extensions;
|
||||
using System.Collections.Concurrent;
|
||||
using Newtonsoft.Json;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace BreCalClient
|
||||
{
|
||||
@ -129,7 +131,13 @@ namespace BreCalClient
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
this.labelLoginResult.Content = ex.Message;
|
||||
if ((ex.ErrorContent != null && ((string)ex.ErrorContent).StartsWith("{"))) {
|
||||
Error? anError = JsonConvert.DeserializeObject<Error>((string)ex.ErrorContent);
|
||||
this.labelLoginResult.Content = anError?.Message ?? ex.Message;
|
||||
}
|
||||
else {
|
||||
this.labelLoginResult.Content = ex.Message;
|
||||
}
|
||||
labelGeneralStatus.Text = $"Connection {ConnectionStatus.FAILED}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>0.9.6.0</ApplicationVersion>
|
||||
<ApplicationVersion>1.0.0.0</ApplicationVersion>
|
||||
<BootstrapperEnabled>True</BootstrapperEnabled>
|
||||
<Configuration>Debug</Configuration>
|
||||
<CreateDesktopShortcut>True</CreateDesktopShortcut>
|
||||
|
||||
@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>0.9.6.0</ApplicationVersion>
|
||||
<ApplicationVersion>1.0.0.0</ApplicationVersion>
|
||||
<BootstrapperEnabled>False</BootstrapperEnabled>
|
||||
<Configuration>Release</Configuration>
|
||||
<CreateWebPageOnPublish>True</CreateWebPageOnPublish>
|
||||
@ -39,6 +39,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<SuiteName>Bremen calling client</SuiteName>
|
||||
<SupportUrl>https://www.bsmd-emswe.eu/</SupportUrl>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SkipPublishVerification>false</SkipPublishVerification>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PublishFile Include="containership.ico">
|
||||
|
||||
@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>0.9.6.*</ApplicationVersion>
|
||||
<ApplicationVersion>1.0.0.*</ApplicationVersion>
|
||||
<BootstrapperEnabled>True</BootstrapperEnabled>
|
||||
<Configuration>Debug</Configuration>
|
||||
<CreateDesktopShortcut>True</CreateDesktopShortcut>
|
||||
|
||||
@ -182,7 +182,7 @@
|
||||
<value>Ändern</value>
|
||||
</data>
|
||||
<data name="textChangePassword" xml:space="preserve">
|
||||
<value>Password ändern</value>
|
||||
<value>Passwort ändern</value>
|
||||
</data>
|
||||
<data name="textClose" xml:space="preserve">
|
||||
<value>Schliessen</value>
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:p = "clr-namespace:BreCalClient.Resources"
|
||||
xmlns:sets="clr-namespace:BreCalClient.Properties"
|
||||
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
|
||||
mc:Ignorable="d"
|
||||
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="120" d:DesignWidth="800" Loaded="UserControl_Loaded">
|
||||
<Border BorderBrush="LightGray" Margin="1" BorderThickness="1">
|
||||
<Grid>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<applicationSettings>
|
||||
<RoleEditor.Properties.Settings>
|
||||
<setting name="ConnectionString" serializeAs="String">
|
||||
<value>Server=localhost;User ID=ds;Password=HalloWach_2323XXL!!;Database=bremen_calling_devel;Port=33306</value>
|
||||
<value>Server=localhost;User ID=ds;Password=HalloWach_2323XXL!!;Database=bremen_calling;Port=33306</value>
|
||||
</setting>
|
||||
</RoleEditor.Properties.Settings>
|
||||
</applicationSettings>
|
||||
|
||||
@ -31,6 +31,7 @@ from BreCal.stubs.times_tug import get_times_tug
|
||||
from BreCal.stubs.times_full import get_times_full_simple
|
||||
from BreCal.stubs.df_times import get_df_times
|
||||
|
||||
from BreCal.services.schedule_routines import setup_schedule, run_schedule_permanently_in_background
|
||||
|
||||
def create_app(test_config=None):
|
||||
|
||||
@ -60,15 +61,20 @@ def create_app(test_config=None):
|
||||
app.register_blueprint(user.bp)
|
||||
|
||||
|
||||
logging.basicConfig(filename='brecal.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s')
|
||||
logging.basicConfig(filename='brecaltest.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s')
|
||||
local_db.initPool(os.path.dirname(app.instance_path))
|
||||
logging.info('App started')
|
||||
|
||||
# Setup Routine jobs (e.g., reevaluation of shipcalls)
|
||||
setup_schedule(update_shipcalls_interval_in_minutes=60)
|
||||
run_schedule_permanently_in_background(latency=30)
|
||||
logging.info('Routine Jobs are defined.')
|
||||
|
||||
return app
|
||||
|
||||
__all__ = [
|
||||
"get_project_root",
|
||||
"ensure_path",
|
||||
"get_project_root",
|
||||
"ensure_path",
|
||||
"execute_test_with_pytest",
|
||||
"execute_coverage_test",
|
||||
"difference_to_then",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from enum import Enum
|
||||
from enum import Enum, IntFlag
|
||||
|
||||
class ParticipantType(Enum):
|
||||
class ParticipantType(IntFlag):
|
||||
"""determines the type of a participant"""
|
||||
NONE = 0
|
||||
BSMD = 1
|
||||
|
||||
@ -1,12 +1,26 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import datetime
|
||||
import typing
|
||||
from BreCal.schemas.model import Shipcall, Ship, Participant, Berth, User, Times
|
||||
from BreCal.database.enums import ParticipantType
|
||||
|
||||
def pandas_series_to_data_model():
|
||||
return
|
||||
|
||||
def set_participant_type(x, participant_df)->int:
|
||||
"""
|
||||
when iterating over each row entry x in the shipcall_participant_map,
|
||||
one can update the 'type' column by extracting the matching data from a participant dataframe
|
||||
|
||||
returns: participant_type
|
||||
"""
|
||||
participant_id = x["participant_id"]
|
||||
participant_type = participant_df.loc[participant_id, "type"]
|
||||
return participant_type
|
||||
|
||||
|
||||
|
||||
class SQLHandler():
|
||||
"""
|
||||
An object that reads SQL queries from the sql_connection and stores it in pandas DataFrames. The object can read all available tables
|
||||
@ -95,6 +109,10 @@ class SQLHandler():
|
||||
|
||||
# update the 'participants' column in 'shipcall'
|
||||
self.initialize_shipcall_participant_list()
|
||||
|
||||
# update the 'type' in shipcall_participants_map
|
||||
# fully deprecated
|
||||
# self.add_participant_type_to_map()
|
||||
return
|
||||
|
||||
def build_full_mysql_df_dict(self, all_schemas):
|
||||
@ -121,6 +139,52 @@ class SQLHandler():
|
||||
lambda x: self.get_participants(x.name),
|
||||
axis=1)
|
||||
return
|
||||
|
||||
def add_participant_type_to_map(self):
|
||||
"""
|
||||
applies a lambda function, where the 'type'-column in the shipcall_participant_map is updated by reading the
|
||||
respective data from the participants. Updates the shipcall_participant_map inplace.
|
||||
"""
|
||||
raise Exception("deprecated! Overwriting the shipcall_participant_map may cause harm, as a participant with multi-flag might be wrongfully assigned to multiple roles simultaneously.")
|
||||
#spm = self.df_dict["shipcall_participant_map"]
|
||||
#participant_df = self.df_dict["participant"]
|
||||
|
||||
#spm.loc[:,"type"] = spm.loc[:].apply(lambda x: set_participant_type(x, participant_df=participant_df),axis=1)
|
||||
#self.df_dict["shipcall_participant_map"] = spm
|
||||
return
|
||||
|
||||
def get_assigned_participants(self, shipcall)->pd.DataFrame:
|
||||
"""return each participant of a respective shipcall, filtered by the shipcall id"""
|
||||
# get the shipcall_participant_map
|
||||
spm = self.df_dict["shipcall_participant_map"]
|
||||
assigned_participants = spm.loc[spm["shipcall_id"]==shipcall.id]
|
||||
return assigned_participants
|
||||
|
||||
def get_assigned_participants_by_type(self, assigned_participants:pd.DataFrame, participant_type:ParticipantType):
|
||||
"""filters a dataframe of assigned_participants by the provided type enumerator"""
|
||||
if isinstance(participant_type,int):
|
||||
participant_type = ParticipantType(participant_type)
|
||||
|
||||
assigned_participants_of_type = assigned_participants.loc[[participant_type in ParticipantType(int(pt_)) for pt_ in list(assigned_participants["type"].values)]]
|
||||
#assigned_participants_of_type = assigned_participants.loc[assigned_participants["type"]==participant_type.value]
|
||||
return assigned_participants_of_type
|
||||
|
||||
def check_if_any_participant_of_type_is_unassigned(self, shipcall, *args:list[ParticipantType])->bool:
|
||||
"""
|
||||
given a list of input arguments, where item is a participant type, the function determines, whether at least one participant
|
||||
was assigned for the type. Function returns a boolean, whether any of the required participants in unassigned.
|
||||
|
||||
This method is extensively used for the validation rules 0001, where the header is checked beforehand to identify, whether
|
||||
the respective participant type is assigned already.
|
||||
"""
|
||||
assigned_participants = self.get_assigned_participants(shipcall)
|
||||
|
||||
unassigned = [] # becomes a list of booleans
|
||||
for participant_type in args:
|
||||
assignments_of_type = self.get_assigned_participants_by_type(assigned_participants, participant_type=participant_type)
|
||||
unassignment = len(assignments_of_type)==0 # a participant type does not exist, when there is no match
|
||||
unassigned.append(unassignment)
|
||||
return any(unassigned) # returns a single boolean, whether ANY of the types is not assigned
|
||||
|
||||
def standardize_model_str(self, model_str:str)->str:
|
||||
"""check if the 'model_str' is valid and apply lowercasing to the string"""
|
||||
@ -159,7 +223,10 @@ class SQLHandler():
|
||||
return all_data
|
||||
|
||||
def df_loc_to_data_model(self, df, id, model_str, loc_type:str="loc"):
|
||||
assert len(df)>0, f"empty dataframe"
|
||||
if not len(df)>0:
|
||||
import warnings
|
||||
warnings.warn(f"empty dataframe in SQLHandler.df_loc_to_data_model for model type: {model_str}\n")
|
||||
return df
|
||||
|
||||
# get a pandas series from the dataframe
|
||||
series = df.loc[id] if loc_type=="loc" else df.iloc[id]
|
||||
@ -174,9 +241,35 @@ class SQLHandler():
|
||||
data = data_model(**data)
|
||||
return data
|
||||
|
||||
def filter_df_by_participant_type(self, df, participant_type:typing.Union[int, ParticipantType])->pd.DataFrame:
|
||||
"""
|
||||
As ParticipantTypes are Flag objects, a dataframe's integer might resemble multiple participant types simultaneously.
|
||||
This function allows for more complex filters, as the IntFlag allows more complex queries
|
||||
|
||||
e.g.:
|
||||
ParticipantType(6) is 2,4 (2+4 = 6)
|
||||
|
||||
Participant(2) in Participant(6) = True # 6 is both, 2 and 4
|
||||
Participant(1) in Participant(6) = False # 6 is both, 2 and 4, but not 1
|
||||
"""
|
||||
if isinstance(participant_type,int):
|
||||
participant_type = ParticipantType(participant_type)
|
||||
filtered_df = df.loc[[participant_type in ParticipantType(df_pt) for df_pt in list(df["participant_type"].values)]]
|
||||
return filtered_df
|
||||
|
||||
def get_times_for_participant_type(self, df_times, participant_type:int):
|
||||
filtered_series = df_times.loc[df_times["participant_type"]==participant_type]
|
||||
assert len(filtered_series)<=1, f"found multiple results"
|
||||
filtered_series = self.filter_df_by_participant_type(df_times, participant_type)
|
||||
#filtered_series = df_times.loc[df_times["participant_type"]==participant_type]
|
||||
|
||||
if len(filtered_series)==0:
|
||||
return None
|
||||
|
||||
if not len(filtered_series)<=1:
|
||||
# correcting the error: ERROR:root:found multiple results
|
||||
# however, a warning will still be issued
|
||||
import warnings
|
||||
warnings.warn(f"found multiple results in function SQLHandler.get_times_for_participant_type\nConsidering only the first match!\nAffected Times Indexes: {filtered_series.index}")
|
||||
|
||||
times = self.df_loc_to_data_model(filtered_series, id=0, model_str='times', loc_type="iloc") # use iloc! to retrieve the first result
|
||||
return times
|
||||
|
||||
@ -198,10 +291,13 @@ class SQLHandler():
|
||||
returns: participant_id_list, where every element is an int
|
||||
"""
|
||||
df = self.df_dict.get("shipcall_participant_map")
|
||||
df = df.set_index('shipcall_id', inplace=False)
|
||||
if 'shipcall_id' in list(df.columns):
|
||||
df = df.set_index('shipcall_id', inplace=False)
|
||||
|
||||
# the 'if' call is needed to ensure, that no Exception is raised, when the shipcall_id is not present in the df
|
||||
participant_id_list = df.loc[shipcall_id, "participant_id"].to_list() if shipcall_id in list(df.index) else []
|
||||
participant_id_list = df.loc[shipcall_id, "participant_id"].tolist() if shipcall_id in list(df.index) else []
|
||||
if not isinstance(participant_id_list,list):
|
||||
participant_id_list = [participant_id_list]
|
||||
return participant_id_list
|
||||
|
||||
def get_times_of_shipcall(self, shipcall)->pd.DataFrame:
|
||||
@ -227,7 +323,8 @@ class SQLHandler():
|
||||
df_times = df_times.loc[~df_times[non_null_column].isnull()] # NOT null filter
|
||||
|
||||
# filter by the agency participant_type
|
||||
times_agency = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]
|
||||
times_agency = self.filter_df_by_participant_type(df_times, ParticipantType.AGENCY.value)
|
||||
#times_agency = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]
|
||||
return times_agency
|
||||
|
||||
def filter_df_by_key_value(self, df, key, value)->pd.DataFrame:
|
||||
@ -237,8 +334,8 @@ class SQLHandler():
|
||||
"""given a dataframe of all agency times, get all unique ship counts, their values (datetime) and the string tags. returns a tuple (values,unique,counts)"""
|
||||
# optional: rounding
|
||||
if rounding is not None:
|
||||
all_df_times.loc[:, query] = all_df_times.loc[:, query].dt.round(rounding) # e.g., 'min'
|
||||
query_time_agency = times_agency[query].iloc[0].round(rounding)# e.g., 'min'
|
||||
all_df_times.loc[:, query] = pd.to_datetime(all_df_times.loc[:, query]).dt.round(rounding) # e.g., 'min' --- # correcting the error: 'AttributeError: Can only use .dt accessor with datetimelike values'
|
||||
query_time_agency = pd.to_datetime(times_agency[query]).iloc[0].round(rounding)# e.g., 'min'
|
||||
|
||||
# after rounding, filter {all_df_times}, so only those, which match the current query are of interest
|
||||
# takes 'times_agency' to sample, which value should match
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
import traceback
|
||||
import pydapper
|
||||
|
||||
from ..schemas import model
|
||||
@ -36,6 +36,7 @@ def GetShipcalls(options):
|
||||
pooledConnection.close()
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(traceback.format_exc())
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
result = {}
|
||||
@ -115,6 +116,7 @@ def PostShipcalls(schemaModel):
|
||||
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(traceback.format_exc())
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
result = {}
|
||||
@ -199,6 +201,7 @@ def PutShipcalls(schemaModel):
|
||||
return json.dumps({"id" : schemaModel["id"]}), 200
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(traceback.format_exc())
|
||||
logging.error(ex)
|
||||
print(ex)
|
||||
result = {}
|
||||
|
||||
81
src/server/BreCal/services/schedule_routines.py
Normal file
81
src/server/BreCal/services/schedule_routines.py
Normal file
@ -0,0 +1,81 @@
|
||||
import logging
|
||||
import pydapper
|
||||
from BreCal.schemas import model
|
||||
from BreCal.local_db import getPoolConnection
|
||||
from BreCal.database.update_database import evaluate_shipcall_state
|
||||
|
||||
import threading
|
||||
import schedule
|
||||
import time
|
||||
|
||||
options = {'past_days':7}
|
||||
|
||||
def UpdateShipcalls(options:dict = {'past_days':2}):
|
||||
"""
|
||||
performs a single update step of every shipcall that matches the criteria.
|
||||
|
||||
1.) opens an SQL connection
|
||||
2.) queries every shipcall that fulfills the {options} criteria
|
||||
3.) iterates over each shipcall
|
||||
4.) closes the SQL connection
|
||||
|
||||
options:
|
||||
key: 'past_days'. Is used to execute a filtered query of all available shipcalls. Defaults to 2 (days)
|
||||
"""
|
||||
try:
|
||||
pooledConnection = getPoolConnection()
|
||||
commands = pydapper.using(pooledConnection)
|
||||
query = ("SELECT id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, "
|
||||
"flags, pier_side, bunkering, replenishing_terminal, replenishing_lock, draft, tidal_window_from, tidal_window_to, rain_sensitive_cargo, recommended_tugs, "
|
||||
"anchored, moored_lock, canceled, evaluation, evaluation_message, created, modified FROM shipcall WHERE ((type = 1 OR type = 3) AND eta >= DATE(NOW() - INTERVAL %d DAY)"
|
||||
"OR (type = 2 AND etd >= DATE(NOW() - INTERVAL %d DAY))) "
|
||||
"ORDER BY eta") % (options["past_days"], options["past_days"])
|
||||
|
||||
# obtain data from the MYSQL database
|
||||
data = commands.query(query, model=model.Shipcall)
|
||||
|
||||
# get the shipcall ids, which are of interest
|
||||
shipcall_ids = [dat.id for dat in data]
|
||||
|
||||
# iterate over each shipcall id and (re-) evaluate it
|
||||
for shipcall_id in shipcall_ids:
|
||||
# apply 'Traffic Light' evaluation to obtain 'GREEN', 'YELLOW' or 'RED' evaluation state. The function internally updates the mysql database
|
||||
evaluate_shipcall_state(mysql_connector_instance=pooledConnection, shipcall_id=shipcall_id) # new_id (last insert id) refers to the shipcall id
|
||||
|
||||
pooledConnection.close()
|
||||
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
return
|
||||
|
||||
def add_function_to_schedule__update_shipcalls(interval_in_minutes:int, options:dict={'past_days':2}):
|
||||
kwargs_ = {"options":options}
|
||||
schedule.every(interval_in_minutes).minutes.do(UpdateShipcalls, **kwargs_)
|
||||
return
|
||||
|
||||
def setup_schedule(update_shipcalls_interval_in_minutes:int=60):
|
||||
schedule.clear() # clear all routine jobs. This prevents jobs from being created multiple times
|
||||
|
||||
# update the evaluation state in every recent shipcall
|
||||
add_function_to_schedule__update_shipcalls(update_shipcalls_interval_in_minutes)
|
||||
|
||||
# placeholder: create/send notifications
|
||||
# add_function_to_schedule__send_notifications(...)
|
||||
return
|
||||
|
||||
|
||||
def run_schedule_permanently(latency:int=30):
|
||||
"""permanently runs the scheduler. Any time a routine task is 'due', it will be executed. There is an interval between each iteration to reduce workload"""
|
||||
while True:
|
||||
schedule.run_pending()
|
||||
time.sleep(latency)
|
||||
return
|
||||
|
||||
def run_schedule_permanently_in_background(latency:int=30, thread_name:str="run_schedule_in_background"):
|
||||
"""create a (daemon) thread in the background, which permanently executes the scheduled functions"""
|
||||
thread_names = [thread_.name for thread_ in threading.enumerate()]
|
||||
if not thread_name in thread_names:
|
||||
bg_thread = threading.Thread(target=run_schedule_permanently, name=thread_name, kwargs={"latency":latency})
|
||||
bg_thread.daemon = True # 'daemon': stops, when Python's main thread stops.
|
||||
bg_thread.start()
|
||||
return
|
||||
@ -10,18 +10,18 @@ from BreCal.database.enums import StatusFlags
|
||||
# a human interpretable dictionary for error messages. In this case, the English language is preferred
|
||||
error_message_dict = {
|
||||
# 0001 A-M
|
||||
"validation_rule_fct_missing_time_agency_berth_eta":"The shipcall arrives in less than 20 hours, but there are still missing times by the agency. Please add the estimated time of arrival (ETA) {Rule #0001A}", # A
|
||||
"validation_rule_fct_missing_time_agency_berth_etd":"The shipcall departs in less than 20 hours, but there are still missing times by the agency. Please add the estimated time of departure (ETD) {Rule #0001B}", # B
|
||||
"validation_rule_fct_missing_time_mooring_berth_eta":"The shipcall arrives in less than 16 hours, but there are still missing times by the mooring. Please add the estimated time of arrival (ETA) {Rule #0001C}", # C
|
||||
"validation_rule_fct_missing_time_mooring_berth_etd":"The shipcall departs in less than 16 hours, but there are still missing times by the mooring. Please add the estimated time of departure (ETD) {Rule #0001D}", # D
|
||||
"validation_rule_fct_missing_time_portadministration_berth_eta":"The shipcall arrives in less than 16 hours, but there are still missing times by the port administration. Please add the estimated time of arrival (ETA) {Rule #0001F}", # F
|
||||
"validation_rule_fct_missing_time_portadministration_berth_etd":"The shipcall departs in less than 16 hours, but there are still missing times by the port administration. Please add the estimated time of departure (ETD) {Rule #0001G}", # G
|
||||
"validation_rule_fct_missing_time_pilot_berth_eta":"The shipcall arrives in less than 16 hours, but there are still missing times by the pilot. Please add the estimated time of arrival (ETA) {Rule #0001H}", # H
|
||||
"validation_rule_fct_missing_time_pilot_berth_etd":"The shipcall departs in less than 16 hours, but there are still missing times by the pilot. Please add the estimated time of departure (ETD) {Rule #0001I}", # I
|
||||
"validation_rule_fct_missing_time_tug_berth_eta":"The shipcall arrives in less than 16 hours, but there are still missing times by the tugs. Please add the estimated time of arrival (ETA) {Rule #0001J}", # J
|
||||
"validation_rule_fct_missing_time_tug_berth_etd":"The shipcall departs in less than 16 hours, but there are still missing times by the tugs. Please add the estimated time of departure (ETD) {Rule #0001K}", # K
|
||||
"validation_rule_fct_missing_time_terminal_berth_eta":"The shipcall arrives in less than 16 hours, but there are still missing times by the terminal. Please add the estimated time of arrival (ETA) {Rule #0001L}", # L
|
||||
"validation_rule_fct_missing_time_terminal_berth_etd":"The shipcall departs in less than 16 hours, but there are still missing times by the terminal. Please add the estimated time of departure (ETD) {Rule #0001M}", # M
|
||||
"validation_rule_fct_missing_time_agency_berth_eta":"Shipcall arrives soon (<20 hours). The agency did not provide a time yet (ETA) {Rule #0001A}", # A
|
||||
"validation_rule_fct_missing_time_agency_berth_etd":"Shipcall departs soon (<20 hours). The agency did not provide a time yet (ETD) {Rule #0001B}", # B
|
||||
"validation_rule_fct_missing_time_mooring_berth_eta":"Shipcall arrives soon (<16 hours). The mooring did not provide a time yet (ETA) {Rule #0001C}", # C
|
||||
"validation_rule_fct_missing_time_mooring_berth_etd":"Shipcall departs soon (<16 hours). The mooring did not provide a time yet (ETD) {Rule #0001D}", # D
|
||||
"validation_rule_fct_missing_time_portadministration_berth_eta":"Shipcall arrives soon (<16 hours). The port administration did not provide a time yet (ETA) {Rule #0001F}", # F
|
||||
"validation_rule_fct_missing_time_portadministration_berth_etd":"Shipcall departs soon (<20 hours). The port administration did not provide a time yet (ETD) {Rule #0001G}", # G
|
||||
"validation_rule_fct_missing_time_pilot_berth_eta":"Shipcall arrives soon (<16 hours). The pilot did not provide a time yet (ETA) {Rule #0001H}", # H
|
||||
"validation_rule_fct_missing_time_pilot_berth_etd":"Shipcall departs soon (<20 hours). The pilot did not provide a time yet (ETD) {Rule #0001I}", # I
|
||||
"validation_rule_fct_missing_time_tug_berth_eta":"Shipcall arrives soon (<16 hours). The tugs did not provide a time yet (ETA) {Rule #0001J}", # J
|
||||
"validation_rule_fct_missing_time_tug_berth_etd":"Shipcall departs soon (<20 hours). The tugs did not provide a time yet (ETD) {Rule #0001K}", # K
|
||||
"validation_rule_fct_missing_time_terminal_berth_eta":"Shipcall arrives soon (<16 hours). The terminal did not provide a time yet (ETA) {Rule #0001L}", # L
|
||||
"validation_rule_fct_missing_time_terminal_berth_etd":"Shipcall departs soon (<20 hours). The terminal did not provide a time yet (ETD) {Rule #0001M}", # M
|
||||
|
||||
# 0002 A+B+C
|
||||
"validation_rule_fct_shipcall_incoming_participants_disagree_on_eta":"There are deviating times between agency, mooring, port authority, pilot and tug for the estimated time of arrival (ETA) {Rule #0002A}",
|
||||
@ -63,6 +63,10 @@ class ValidationRuleBaseFunctions():
|
||||
returns: string
|
||||
"""
|
||||
return self.error_message_dict.get(key,key)
|
||||
|
||||
def get_no_violation_default_output(self):
|
||||
"""return the default output of a validation function with no validation: a tuple of (GREEN state, None)"""
|
||||
return (StatusFlags.GREEN, None)
|
||||
|
||||
def check_time_delta_violation_query_time_to_now(self, query_time:pd.Timestamp, key_time:pd.Timestamp, threshold:float)->bool:
|
||||
"""
|
||||
@ -95,9 +99,8 @@ class ValidationRuleBaseFunctions():
|
||||
delta = self.time_logic.time_delta_from_now_to_tgt(tgt_time=query_time, unit="m")
|
||||
|
||||
# a violation occurs, when the delta (in minutes) exceeds the specified threshold of a participant
|
||||
# to prevent past-events from triggering violations, negative values are ignored
|
||||
# Violation, if 0 <= delta <= threshold
|
||||
violation_state = (delta >= 0) and (delta<=threshold)
|
||||
# Violation, if delta <= threshold
|
||||
violation_state = (delta<=threshold)
|
||||
return violation_state
|
||||
|
||||
def check_participants_agree_on_estimated_time(self, shipcall, query, df_times, applicable_shipcall_type)->bool:
|
||||
@ -207,22 +210,26 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_agency.eta_berth is filled in.
|
||||
- Measures the difference between 'now' and 'shipcall.eta'.
|
||||
"""
|
||||
# check, if the header is filled in (agency)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value])]) != 1:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.INCOMING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
query_time = shipcall.eta
|
||||
key_time = times_agency.eta_berth
|
||||
key_time = times_agency.eta_berth if times_agency is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.AGENCY
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_agency_berth_eta"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_agency_berth_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -235,22 +242,26 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_agency.etd_berth is filled in.
|
||||
- Measures the difference between 'now' and 'shipcall.etd'.
|
||||
"""
|
||||
# check, if the header is filled in (agency)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value])]) != 1:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
query_time = shipcall.etd
|
||||
key_time = times_agency.etd_berth
|
||||
key_time = times_agency.etd_berth if times_agency is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.AGENCY
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_agency_berth_etd"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_mooring_berth_eta(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -263,24 +274,28 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_mooring.eta_berth is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.eta_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & MOORING)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.MOORING.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.INCOMING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.MOORING])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_mooring = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.MOORING.value)
|
||||
|
||||
query_time = times_agency.eta_berth
|
||||
key_time = times_mooring.eta_berth
|
||||
query_time = times_agency.eta_berth if times_agency is not None else None
|
||||
key_time = times_mooring.eta_berth if times_mooring is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.MOORING
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_mooring_berth_eta"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_mooring_berth_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -293,24 +308,28 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_mooring.etd_berth is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.etd_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & MOORING)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.MOORING.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.MOORING])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_mooring = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.MOORING.value)
|
||||
|
||||
query_time = times_agency.etd_berth
|
||||
key_time = times_mooring.etd_berth
|
||||
query_time = times_agency.etd_berth if times_agency is not None else None
|
||||
key_time = times_mooring.etd_berth if times_mooring is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.MOORING
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_mooring_berth_etd"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_portadministration_berth_eta(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -323,24 +342,28 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_port_administration.eta_berth is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.eta_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & PORT_ADMINISTRATION)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.PORT_ADMINISTRATION.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.INCOMING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.PORT_ADMINISTRATION])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_port_administration = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.PORT_ADMINISTRATION.value)
|
||||
|
||||
query_time = times_agency.eta_berth
|
||||
key_time = times_port_administration.eta_berth
|
||||
query_time = times_agency.eta_berth if times_agency is not None else None
|
||||
key_time = times_port_administration.eta_berth if times_port_administration is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.PORT_ADMINISTRATION
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_portadministration_berth_eta"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_portadministration_berth_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -353,24 +376,29 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_port_administration.etd_berth is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.etd_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & PORT_ADMINISTRATION)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.PORT_ADMINISTRATION.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.PORT_ADMINISTRATION])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
# when there are no times, the function returns None
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_port_administration = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.PORT_ADMINISTRATION.value)
|
||||
|
||||
query_time = times_agency.etd_berth
|
||||
key_time = times_port_administration.etd_berth
|
||||
query_time = times_agency.etd_berth if times_agency is not None else None
|
||||
key_time = times_port_administration.etd_berth if times_port_administration is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.PORT_ADMINISTRATION
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_portadministration_berth_etd"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_pilot_berth_eta(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -383,24 +411,28 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_pilot.eta_berth is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.eta_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & PILOT)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.PILOT.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.INCOMING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.PILOT])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_pilot = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.PILOT.value)
|
||||
|
||||
query_time = times_agency.eta_berth
|
||||
key_time = times_pilot.eta_berth
|
||||
query_time = times_agency.eta_berth if times_agency is not None else None
|
||||
key_time = times_pilot.eta_berth if times_pilot is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.PILOT
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_pilot_berth_eta"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_pilot_berth_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -413,24 +445,28 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_pilot.etd_berth is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.etd_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & PILOT)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.PILOT.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.PILOT])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_pilot = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.PILOT.value)
|
||||
|
||||
query_time = times_agency.etd_berth
|
||||
key_time = times_pilot.etd_berth
|
||||
query_time = times_agency.etd_berth if times_agency is not None else None
|
||||
key_time = times_pilot.etd_berth if times_pilot is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.PILOT
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_pilot_berth_etd"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_tug_berth_eta(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -443,24 +479,28 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_tug.eta_berth is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.eta_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & TUG)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.TUG.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.INCOMING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.TUG])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_tug = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TUG.value)
|
||||
|
||||
query_time = times_agency.eta_berth
|
||||
key_time = times_tug.eta_berth
|
||||
query_time = times_agency.eta_berth if times_agency is not None else None
|
||||
key_time = times_tug.eta_berth if times_tug is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.TUG
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_tug_berth_eta"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_tug_berth_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -473,24 +513,28 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
- Checks, if times_tug.etd_berth is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.etd_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & TUG)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.TUG.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.TUG])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_tug = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TUG.value)
|
||||
|
||||
query_time = times_agency.etd_berth
|
||||
key_time = times_tug.etd_berth
|
||||
query_time = times_agency.etd_berth if times_agency is not None else None
|
||||
key_time = times_tug.etd_berth if times_tug is not None else None
|
||||
threshold = ParticipantwiseTimeDelta.TUG
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_tug_berth_etd"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_terminal_berth_eta(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -500,27 +544,31 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
a certain threshold (e.g., 20 hours), a violation occurs
|
||||
|
||||
0001-L:
|
||||
- Checks, if times_terminal.eta_berth is filled in.
|
||||
- Checks, if times_terminal.operations_start is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.eta_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & terminal)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.TERMINAL.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.INCOMING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.TERMINAL])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_terminal = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TERMINAL.value)
|
||||
|
||||
query_time = times_agency.eta_berth
|
||||
key_time = times_terminal.eta_berth
|
||||
query_time = times_agency.eta_berth if times_agency is not None else None
|
||||
key_time = times_terminal.operations_start if times_terminal is not None else None # eta_berth does not exist in times_terminal! Instead, it is called operations_start
|
||||
threshold = ParticipantwiseTimeDelta.TERMINAL
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_terminal_berth_eta"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_missing_time_terminal_berth_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -530,28 +578,31 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
a certain threshold (e.g., 20 hours), a violation occurs
|
||||
|
||||
0001-M:
|
||||
- Checks, if times_terminal.etd_berth is filled in.
|
||||
- Checks, if times_terminal.operations_end is filled in.
|
||||
- Measures the difference between 'now' and 'times_agency.etd_berth'.
|
||||
"""
|
||||
# check, if the header is filled in (agency & terminal)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.TERMINAL.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in
|
||||
unassigned = self.sql_handler.check_if_any_participant_of_type_is_unassigned(shipcall, *[ParticipantType.AGENCY, ParticipantType.TERMINAL])
|
||||
if unassigned:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# preparation: obtain the correct times of the participant, define the query time and the key time
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_terminal = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TERMINAL.value)
|
||||
|
||||
query_time = times_agency.etd_berth
|
||||
key_time = times_terminal.etd_berth
|
||||
query_time = times_agency.etd_berth if times_agency is not None else None
|
||||
key_time = times_terminal.operations_end if times_terminal is not None else None # etd_berth does not exist in times_terminal! Instead, it is called operations_end
|
||||
threshold = ParticipantwiseTimeDelta.TERMINAL
|
||||
violation_state = self.check_time_delta_violation_query_time_to_now(query_time=query_time, key_time=key_time, threshold=threshold)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_missing_time_terminal_berth_etd"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_shipcall_incoming_participants_disagree_on_eta(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -571,10 +622,10 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_shipcall_incoming_participants_disagree_on_eta"
|
||||
return (StatusFlags.RED, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_shipcall_outgoing_participants_disagree_on_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -594,10 +645,10 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_shipcall_outgoing_participants_disagree_on_etd"
|
||||
return (StatusFlags.RED, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_shipcall_shifting_participants_disagree_on_eta_or_etd(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -631,10 +682,10 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
violation_state = (violation_state_eta) or (violation_state_etd)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_shipcall_shifting_participants_disagree_on_eta_or_etd"
|
||||
return (StatusFlags.RED, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_eta_time_not_in_operation_window(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -645,25 +696,31 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
query time: eta_berth (times_agency)
|
||||
start_time & end_time: operations_start & operations_end (times_terminal)
|
||||
"""
|
||||
if not shipcall.type in [ShipcallType.INCOMING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in (agency & terminal)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.TERMINAL.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if len(df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]) != 1:
|
||||
return self.get_no_violation_default_output() # rule not applicable
|
||||
|
||||
if len(df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value]) != 1:
|
||||
return self.get_no_violation_default_output() # rule not applicable
|
||||
|
||||
# get agency & terminal times
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_terminal = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TERMINAL.value)
|
||||
|
||||
if self.check_is_not_a_time_or_is_none(times_terminal.operations_start) or self.check_is_not_a_time_or_is_none(times_agency.eta_berth):
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, whether the start of operations is AFTER the estimated arrival time
|
||||
violation_state = times_terminal.operations_start < times_agency.eta_berth
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_eta_time_not_in_operation_window"
|
||||
return (StatusFlags.RED, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_etd_time_not_in_operation_window(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -674,25 +731,31 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
query time: eta_berth (times_agency)
|
||||
start_time & end_time: operations_start & operations_end (times_terminal)
|
||||
"""
|
||||
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in (agency & terminal)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.TERMINAL.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if len(df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]) != 1:
|
||||
return self.get_no_violation_default_output() # rule not applicable
|
||||
|
||||
if len(df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value]) != 1:
|
||||
return self.get_no_violation_default_output() # rule not applicable
|
||||
|
||||
# get agency & terminal times
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_terminal = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TERMINAL.value)
|
||||
|
||||
if self.check_is_not_a_time_or_is_none(times_terminal.operations_end) or self.check_is_not_a_time_or_is_none(times_agency.etd_berth):
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, whether the end of operations is AFTER the estimated departure time
|
||||
violation_state = times_terminal.operations_end > times_agency.etd_berth
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_etd_time_not_in_operation_window"
|
||||
return (StatusFlags.RED, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_eta_time_not_in_tidal_window(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -703,24 +766,27 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
query time: eta_berth (times_agency)
|
||||
start_time & end_time: tidal_window_from & tidal_window_to (shipcall)
|
||||
"""
|
||||
if not shipcall.type in [ShipcallType.INCOMING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in (agency)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value])]) != 1:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
|
||||
# requirements: tidal window (from & to) is filled in
|
||||
if self.check_is_not_a_time_or_is_none(shipcall.tidal_window_from) or self.check_is_not_a_time_or_is_none(shipcall.tidal_window_to) or self.check_is_not_a_time_or_is_none(times_agency.eta_berth): # 202310310: note: this should check times_agency, shouldn't it?
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, whether the query time is between start & end time
|
||||
# a violation is observed, when the time is NOT between start & end
|
||||
violation_state = not self.time_logic.time_inbetween(query_time=times_agency.eta_berth, start_time=shipcall.tidal_window_from, end_time=shipcall.tidal_window_to)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_eta_time_not_in_tidal_window"
|
||||
return (StatusFlags.RED, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_etd_time_not_in_tidal_window(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -731,24 +797,27 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
query time: eta_berth (times_agency)
|
||||
start_time & end_time: tidal_window_from & tidal_window_to (shipcall)
|
||||
"""
|
||||
if not shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, if the header is filled in (agency)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value])]) != 1:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
|
||||
# requirements: tidal window (from & to) is filled in
|
||||
if self.check_is_not_a_time_or_is_none(shipcall.tidal_window_from) or self.check_is_not_a_time_or_is_none(shipcall.tidal_window_to) or self.check_is_not_a_time_or_is_none(times_agency.etd_berth): # 202310310: note: this should check times_agency, shouldn't it?
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# check, whether the query time is between start & end time
|
||||
# a violation is observed, when the time is NOT between start & end
|
||||
violation_state = not self.time_logic.time_inbetween(query_time=times_agency.etd_berth, start_time=shipcall.tidal_window_from, end_time=shipcall.tidal_window_to)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_etd_time_not_in_tidal_window"
|
||||
return (StatusFlags.RED, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_too_many_identical_eta_times(self, shipcall, df_times, rounding = "min", maximum_threshold = 3, all_times_agency=None, *args, **kwargs):
|
||||
"""
|
||||
@ -759,17 +828,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
times_agency = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]
|
||||
# check, if the header is filled in (agency)
|
||||
if len(times_agency) != 1:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# when ANY of the unique values exceeds the threshold, a violation is observed
|
||||
query = "eta_berth"
|
||||
violation_state = self.check_unique_shipcall_counts(query, times_agency=times_agency, rounding=rounding, maximum_threshold=maximum_threshold, all_times_agency=all_times_agency)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_too_many_identical_eta_times"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_too_many_identical_etd_times(self, shipcall, df_times, rounding = "min", maximum_threshold = 3, all_times_agency=None, *args, **kwargs):
|
||||
"""
|
||||
@ -780,17 +849,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
times_agency = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]
|
||||
# check, if the header is filled in (agency)
|
||||
if len(times_agency) != 1:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# when ANY of the unique values exceeds the threshold, a violation is observed
|
||||
query = "etd_berth"
|
||||
violation_state = self.check_unique_shipcall_counts(query, times_agency=times_agency, rounding=rounding, maximum_threshold=maximum_threshold, all_times_agency=all_times_agency)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_too_many_identical_etd_times"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_agency_and_terminal_berth_id_disagreement(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -799,31 +868,34 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
Description: This validation rule checks, whether agency and terminal agree with their designated berth place by checking berth_id.
|
||||
"""
|
||||
# check, if the header is filled in (agency & terminal)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.TERMINAL.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if len(df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]) == 0:
|
||||
return self.get_no_violation_default_output() # rule not applicable
|
||||
|
||||
if len(df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value]) == 0:
|
||||
return self.get_no_violation_default_output() # rule not applicable
|
||||
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_terminal = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TERMINAL.value)
|
||||
|
||||
# when one of the two values is null, the state is GREEN
|
||||
if (times_agency.berth_id is None) or (times_terminal.berth_id is None):
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# when one of the two values is null, the state is GREEN
|
||||
if (pd.isnull(times_agency.berth_id)) or (pd.isnull(times_terminal.berth_id)):
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
if shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# only incoming shipcalls matter. The other ones are not relevant for the berth selection
|
||||
violation_state = times_agency.berth_id!=times_terminal.berth_id
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_agency_and_terminal_berth_id_disagreement"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
def validation_rule_fct_agency_and_terminal_pier_side_disagreement(self, shipcall, df_times, *args, **kwargs):
|
||||
"""
|
||||
@ -832,30 +904,33 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
|
||||
Description: This validation rule checks, whether agency and terminal agree with their designated pier side by checking pier_side.
|
||||
"""
|
||||
# check, if the header is filled in (agency & terminal)
|
||||
if len(df_times.loc[df_times["participant_type"].isin([ParticipantType.AGENCY.value, ParticipantType.TERMINAL.value])]) != 2:
|
||||
return (StatusFlags.GREEN, None)
|
||||
if len(df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value]) == 0:
|
||||
return self.get_no_violation_default_output() # rule not applicable
|
||||
|
||||
if len(df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value]) == 0:
|
||||
return self.get_no_violation_default_output() # rule not applicable
|
||||
|
||||
times_agency = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.AGENCY.value)
|
||||
times_terminal = self.sql_handler.get_times_for_participant_type(df_times, participant_type=ParticipantType.TERMINAL.value)
|
||||
|
||||
# when one of the two values is null, the state is GREEN
|
||||
if (times_agency.pier_side is None) or (times_terminal.pier_side is None):
|
||||
return (StatusFlags.GREEN, None)
|
||||
if (shipcall.pier_side is None) or (times_terminal.pier_side is None):
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# when one of the two values is null, the state is GREEN
|
||||
if (pd.isnull(times_agency.pier_side)) or (pd.isnull(times_terminal.pier_side)):
|
||||
return (StatusFlags.GREEN, None)
|
||||
if (pd.isnull(shipcall.pier_side)) or (pd.isnull(times_terminal.pier_side)):
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
# only incoming shipcalls matter. The other ones are not relevant for the pier_side selection
|
||||
if shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
violation_state = times_agency.pier_side!=times_terminal.pier_side
|
||||
violation_state = bool(shipcall.pier_side)!=bool(times_terminal.pier_side)
|
||||
|
||||
if violation_state:
|
||||
validation_name = inspect.currentframe().f_code.co_name
|
||||
validation_name = "validation_rule_fct_agency_and_terminal_pier_side_disagreement"
|
||||
return (StatusFlags.YELLOW, validation_name)
|
||||
else:
|
||||
return (StatusFlags.GREEN, None)
|
||||
return self.get_no_violation_default_output()
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import copy
|
||||
import re
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from BreCal.database.enums import StatusFlags
|
||||
@ -29,7 +30,14 @@ class ValidationRules(ValidationRuleFunctions):
|
||||
returns: (evaluation_state, violations)
|
||||
"""
|
||||
# prepare df_times, which every validation rule tends to use
|
||||
df_times = self.sql_handler.df_dict.get('times') # -> pd.DataFrame
|
||||
df_times = self.sql_handler.df_dict.get('times', pd.DataFrame()) # -> pd.DataFrame
|
||||
|
||||
if len(df_times)==0:
|
||||
return (StatusFlags.GREEN.value, [])
|
||||
|
||||
spm = self.sql_handler.df_dict["shipcall_participant_map"]
|
||||
if len(spm.loc[spm["shipcall_id"]==shipcall.id])==0:
|
||||
return (StatusFlags.GREEN.value, [])
|
||||
|
||||
# filter by shipcall id
|
||||
df_times = self.sql_handler.get_times_of_shipcall(shipcall)
|
||||
@ -43,15 +51,9 @@ class ValidationRules(ValidationRuleFunctions):
|
||||
|
||||
# 'translate' all error codes into readable, human-understandable format.
|
||||
evaluation_results = [(state, self.describe_error_message(msg)) for (state, msg) in evaluation_results]
|
||||
|
||||
""" # deprecated
|
||||
# check, if ANY of the evaluation results (evaluation_state) is larger than the .GREEN state. This means, that .YELLOW and .RED
|
||||
# would return 'True'. Numpy arrays and functions are used to accelerate the comparison.
|
||||
# np.any returns a boolean.
|
||||
#evaluation_state = not np.any(np.greater(np.array([result[0] for result in evaluation_results]), ValidationRuleState.GREEN))
|
||||
"""
|
||||
|
||||
# check, what the maximum state flag is and return it
|
||||
evaluation_state = np.max(np.array([result[0].value for result in evaluation_results])) if len(evaluation_results)>0 else 1
|
||||
evaluation_state = np.max(np.array([result[0].value for result in evaluation_results])) if len(evaluation_results)>0 else StatusFlags.GREEN.value
|
||||
evaluation_verbosity = [result[1] for result in evaluation_results]
|
||||
return (evaluation_state, evaluation_verbosity)
|
||||
|
||||
@ -74,11 +76,27 @@ class ValidationRules(ValidationRuleFunctions):
|
||||
|
||||
# unbundle individual results. evaluation_state becomes an integer, violation
|
||||
evaluation_state = [StatusFlags(res[0]).value for res in results]
|
||||
violations = [",".join(res[1]) if len(res[1])>0 else None for res in results]
|
||||
violations = [",\r\n".join(res[1]) if len(res[1])>0 else None for res in results]
|
||||
violations = [self.concise_evaluation_message_if_too_long(violation) for violation in violations]
|
||||
|
||||
shipcall_df.loc[:,"evaluation"] = evaluation_state
|
||||
shipcall_df.loc[:,"evaluation_message"] = violations
|
||||
return shipcall_df
|
||||
|
||||
def concise_evaluation_message_if_too_long(self, violation):
|
||||
"""
|
||||
when many validation rules are violated at once, the resulting evaluation message may exceed 512 characters (which the mysql database allows)
|
||||
in these cases, use a regular expression to provide a concise message, where the 'concise' description is only the list of violated rools
|
||||
"""
|
||||
if violation is None:
|
||||
return violation
|
||||
|
||||
if len(violation)>=512:
|
||||
concise = re.findall(r'{(.*?)\}', violation)
|
||||
|
||||
# e.g.: Evaluation message too long. Violated Rules: ['Rule #0001C', 'Rule #0001H', 'Rule #0001F', 'Rule #0001G', 'Rule #0001L', 'Rule #0001M', 'Rule #0001J', 'Rule #0001K']
|
||||
violation = f"Evaluation message too long. Violated Rules: {concise}"
|
||||
return violation
|
||||
|
||||
def determine_validation_state(self) -> str:
|
||||
"""
|
||||
|
||||
@ -5,12 +5,16 @@ import logging
|
||||
sys.path.insert(0, '/var/www/brecal/src/server')
|
||||
sys.path.insert(0, '/var/www/venv/lib/python3.10/site-packages/')
|
||||
|
||||
import schedule
|
||||
|
||||
# set the key
|
||||
os.environ['SECRET_KEY'] = 'zdiTz8P3jXOc7jztIQAoelK4zztyuCpJ'
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
||||
|
||||
# Set up Scheduled Jobs
|
||||
|
||||
# Import and run the Flask app
|
||||
from BreCal import create_app
|
||||
application = create_app()
|
||||
application = create_app()
|
||||
|
||||
@ -117,6 +117,19 @@ def test_validation_rule_fct_missing_time_agency_berth_eta__shipcall_eta_dangero
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "eta_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_agency_berth_eta(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -137,6 +150,19 @@ def test_validation_rule_fct_missing_time_agency_berth_eta__shipcall_eta_distant
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "eta_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_agency_berth_eta(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -157,6 +183,19 @@ def test_validation_rule_fct_missing_time_agency_berth_eta__shipcall_eta_is_unde
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "eta_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_agency_berth_eta(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -172,11 +211,25 @@ def test_validation_rule_fct_missing_time_agency_berth_etd__shipcall_etd_is_unde
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# the shipcall etd is 'soon'
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
shipcall.etd = datetime.datetime.now() + datetime.timedelta(minutes=ParticipantwiseTimeDelta.AGENCY-10)
|
||||
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "etd_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_agency_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -197,6 +250,21 @@ def test_validation_rule_fct_missing_time_mooring_berth_eta__shipcall_soon_but_p
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.MOORING.value, "eta_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
mooring_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.MOORING.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":mooring_participant_id, "type":ParticipantType.MOORING.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_mooring_berth_eta(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -211,6 +279,7 @@ def test_validation_rule_fct_missing_time_mooring_berth_etd__shipcall_soon_but_p
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
@ -219,6 +288,21 @@ def test_validation_rule_fct_missing_time_mooring_berth_etd__shipcall_soon_but_p
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.MOORING.value, "etd_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
mooring_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.MOORING.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":mooring_participant_id, "type":ParticipantType.MOORING.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_mooring_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -241,6 +325,21 @@ def test_validation_rule_fct_missing_time_portadministration_berth_eta__shipcall
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.PORT_ADMINISTRATION.value, "eta_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
pa_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.PORT_ADMINISTRATION.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":pa_participant_id, "type":ParticipantType.PORT_ADMINISTRATION.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_portadministration_berth_eta(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -255,6 +354,7 @@ def test_validation_rule_fct_missing_time_portadministration_berth_etd__shipcall
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.SHIFTING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
@ -263,6 +363,21 @@ def test_validation_rule_fct_missing_time_portadministration_berth_etd__shipcall
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.PORT_ADMINISTRATION.value, "etd_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
pa_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.PORT_ADMINISTRATION.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":pa_participant_id, "type":ParticipantType.PORT_ADMINISTRATION.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_portadministration_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -283,6 +398,21 @@ def test_validation_rule_fct_missing_time_pilot_berth_eta__shipcall_soon_but_par
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value, "eta_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
pilot_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":pilot_participant_id, "type":ParticipantType.PILOT.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_pilot_berth_eta(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -295,6 +425,7 @@ def test_validation_rule_fct_missing_time_pilot_berth_etd__shipcall_soon_but_par
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
@ -303,6 +434,21 @@ def test_validation_rule_fct_missing_time_pilot_berth_etd__shipcall_soon_but_par
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value, "etd_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
pilot_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":pilot_participant_id, "type":ParticipantType.PILOT.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_pilot_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -310,13 +456,127 @@ def test_validation_rule_fct_missing_time_pilot_berth_etd__shipcall_soon_but_par
|
||||
assert state==StatusFlags.YELLOW, f"function should return 'yellow', because the participant did not provide a time and the shipcall takes place soon (according to the agency)"
|
||||
return
|
||||
|
||||
def test_validation_rule_fct_missing_time_pilot_berth_etd__shipcall_soon_but_participant_unassigned__return_green(build_sql_proxy_connection):
|
||||
"""0001-I validation_rule_fct_missing_time_pilot_berth_etd"""
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "etd_berth"] = datetime.datetime.now() + datetime.timedelta(minutes=ParticipantwiseTimeDelta.PILOT-10)
|
||||
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value, "etd_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
|
||||
vr.sql_handler.read_all(vr.sql_handler.all_schemas)
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_pilot_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
# expectation: green state, no msg
|
||||
assert state==StatusFlags.GREEN, f"function should return 'green', because the pilot is not assigned yet"
|
||||
return
|
||||
|
||||
def test_validation_rule_fct_missing_time_pilot_berth_etd__shipcall_soon_but_participant_estimated_time_undefined_multiple_pilot_assignments_due_to_bug(build_sql_proxy_connection):
|
||||
"""
|
||||
0001-I validation_rule_fct_missing_time_pilot_berth_etd. Checks, whether the function still works in case of a buggy input. When there is more than one pilot
|
||||
assignment, the validation rule should still work and return 'yellow' properly.
|
||||
"""
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "etd_berth"] = datetime.datetime.now() + datetime.timedelta(minutes=ParticipantwiseTimeDelta.PILOT-10)
|
||||
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value, "etd_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
pilot_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":pilot_participant_id, "type":ParticipantType.PILOT.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10003, "shipcall_id":shipcall.id, "participant_id":pilot_participant_id, "type":ParticipantType.PILOT.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10004, "shipcall_id":shipcall.id, "participant_id":pilot_participant_id, "type":ParticipantType.PILOT.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_pilot_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
# expectation: green state, no msg
|
||||
assert state==StatusFlags.YELLOW, f"function should return 'yellow', because the participant did not provide a time and the shipcall takes place soon (according to the agency)"
|
||||
return
|
||||
|
||||
def test_validation_rule_fct_missing_time_pilot_berth_etd__agency_and_pilot_assigned_pilot_no_times_returns_yellow(build_sql_proxy_connection):
|
||||
"""
|
||||
0001-I validation_rule_fct_missing_time_pilot_berth_etd. Checks the default behaviour, where an agency's time might exist,
|
||||
while a time by pilot may not exist. In these cases, a yellow state is expected.
|
||||
"""
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "etd_berth"] = datetime.datetime.now() + datetime.timedelta(minutes=ParticipantwiseTimeDelta.PILOT-10)
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
pilot_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value, "participant_id"].iloc[0]
|
||||
|
||||
# set times of PILOT: should not exist
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.PILOT.value,"participant_type"] = ParticipantType.BSMD.value
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":pilot_participant_id, "type":ParticipantType.PILOT.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_pilot_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
# expectation: green state, no msg
|
||||
assert state==StatusFlags.YELLOW, f"function should return 'yellow', because the participant did not provide a time and the shipcall takes place soon (according to the agency)"
|
||||
return
|
||||
|
||||
def test_validation_rule_fct_missing_time_tug_berth_eta__shipcall_soon_but_participant_estimated_time_undefined(build_sql_proxy_connection):
|
||||
"""0001-J validation_rule_fct_missing_time_tug_berth_eta"""
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.INCOMING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
@ -325,6 +585,21 @@ def test_validation_rule_fct_missing_time_tug_berth_eta__shipcall_soon_but_parti
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TUG.value, "eta_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
tug_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.TUG.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":tug_participant_id, "type":ParticipantType.TUG.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_tug_berth_eta(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -339,6 +614,7 @@ def test_validation_rule_fct_missing_time_tug_berth_etd__shipcall_soon_but_parti
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
@ -347,6 +623,21 @@ def test_validation_rule_fct_missing_time_tug_berth_etd__shipcall_soon_but_parti
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TUG.value, "etd_berth"] = None
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
tug_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.TUG.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":tug_participant_id, "type":ParticipantType.TUG.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_tug_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
|
||||
@ -367,7 +658,22 @@ def test_validation_rule_fct_missing_time_terminal_berth_eta__shipcall_soon_but_
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "eta_berth"] = datetime.datetime.now() + datetime.timedelta(minutes=ParticipantwiseTimeDelta.TERMINAL-10)
|
||||
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "eta_berth"] = None
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "operations_start"] = None # previously: eta_berth, which does not exist in times_terminal
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
terminal_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":terminal_participant_id, "type":ParticipantType.TERMINAL.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_terminal_berth_eta(shipcall=shipcall, df_times=df_times)
|
||||
@ -383,13 +689,30 @@ def test_validation_rule_fct_missing_time_terminal_berth_etd__shipcall_soon_but_
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
# according to the agency, a shipcall takes place soon (ETA/ETD)
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "etd_berth"] = datetime.datetime.now() + datetime.timedelta(minutes=ParticipantwiseTimeDelta.TERMINAL-10)
|
||||
|
||||
# set times agency to be undetermined
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "etd_berth"] = None
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "operations_end"] = None # previously: etd_berth, which does not exist in times_terminal
|
||||
|
||||
|
||||
# must adapt the shipcall_participant_map, so it suits the test
|
||||
agency_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "participant_id"].iloc[0]
|
||||
terminal_participant_id = df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "participant_id"].iloc[0]
|
||||
|
||||
spm = vr.sql_handler.df_dict["shipcall_participant_map"]
|
||||
df = pd.DataFrame(
|
||||
[
|
||||
{"id":10001, "shipcall_id":shipcall.id, "participant_id":agency_participant_id, "type":ParticipantType.AGENCY.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None},
|
||||
{"id":10002, "shipcall_id":shipcall.id, "participant_id":terminal_participant_id, "type":ParticipantType.TERMINAL.value, "created":pd.Timestamp(datetime.datetime.now().isoformat()), "modified":None}
|
||||
]
|
||||
)
|
||||
df.set_index("id", inplace=True)
|
||||
spm = pd.concat([spm, df], axis=0, ignore_index=True)
|
||||
vr.sql_handler.df_dict["shipcall_participant_map"] = spm
|
||||
|
||||
# apply the validation rule
|
||||
(state, msg) = vr.validation_rule_fct_missing_time_terminal_berth_etd(shipcall=shipcall, df_times=df_times)
|
||||
@ -602,6 +925,7 @@ def test_validation_rule_fct_etd_time_not_in_operation_window__times_dont_match(
|
||||
"""0003-B validation_rule_fct_etd_time_not_in_operation_window"""
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.SHIFTING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
t0_time = datetime.datetime.now() # reference time for easier readability
|
||||
@ -623,6 +947,7 @@ def test_validation_rule_fct_eta_time_not_in_operation_window_and_validation_rul
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
import random
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.SHIFTING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
t0_time = datetime.datetime.now()
|
||||
@ -718,6 +1043,7 @@ def test_validation_rule_fct_etd_time_not_in_tidal_window__etd_outside_tidal_win
|
||||
"""0004-B validation_rule_fct_etd_time_not_in_tidal_window"""
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
shipcall = get_shipcall_simple()
|
||||
shipcall.type = ShipcallType.OUTGOING.value
|
||||
df_times = get_df_times(shipcall)
|
||||
|
||||
t0_time = datetime.datetime.now()
|
||||
@ -801,7 +1127,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement__agency_
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
shipcall = get_shipcall_simple()
|
||||
df_times = get_df_times(shipcall)
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||
shipcall.pier_side = True
|
||||
# df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = True
|
||||
|
||||
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
||||
@ -813,13 +1140,39 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement__agency_
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
shipcall = get_shipcall_simple()
|
||||
df_times = get_df_times(shipcall)
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||
shipcall.pier_side = True
|
||||
#df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = False
|
||||
|
||||
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
||||
assert code==StatusFlags.YELLOW, f"status should be 'yellow', because agency and terminal do not agree on the selected pier side"
|
||||
return
|
||||
|
||||
def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement__agency_and_terminal_disagree_terminal_is_none(build_sql_proxy_connection):
|
||||
"""0006-B validation_rule_fct_agency_and_terminal_pier_side_disagreement"""
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
shipcall = get_shipcall_simple()
|
||||
df_times = get_df_times(shipcall)
|
||||
shipcall.pier_side = True
|
||||
# df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = None
|
||||
|
||||
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
||||
assert code==StatusFlags.GREEN, f"status should be 'green', because the terminal's pier_side is not provided"
|
||||
return
|
||||
|
||||
def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement__agency_and_terminal_disagree_terminal_is_nan(build_sql_proxy_connection):
|
||||
"""0006-B validation_rule_fct_agency_and_terminal_pier_side_disagreement"""
|
||||
vr = build_sql_proxy_connection['vr']
|
||||
shipcall = get_shipcall_simple()
|
||||
df_times = get_df_times(shipcall)
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value, "pier_side"] = True
|
||||
df_times.loc[df_times["participant_type"]==ParticipantType.TERMINAL.value, "pier_side"] = float("nan")
|
||||
|
||||
(code, msg) = vr.validation_rule_fct_agency_and_terminal_pier_side_disagreement(shipcall=shipcall, df_times=df_times)
|
||||
assert code==StatusFlags.GREEN, f"status should be 'green', because the terminal's pier_side is not provided"
|
||||
return
|
||||
|
||||
|
||||
|
||||
def test_validation_rule_fct_agency_and_terminal_pier_side_agreement(build_sql_proxy_connection):
|
||||
@ -841,7 +1194,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_agreement(build_sql_p
|
||||
t2.participant_type = ParticipantType.TERMINAL.value
|
||||
|
||||
# agreement
|
||||
t1.pier_side = True
|
||||
shipcall.pier_side = True
|
||||
# t1.pier_side = True
|
||||
t2.pier_side = True
|
||||
|
||||
time_objects = [t1, t2]
|
||||
@ -872,7 +1226,8 @@ def test_validation_rule_fct_agency_and_terminal_pier_side_disagreement(build_sq
|
||||
t2.participant_type = ParticipantType.TERMINAL.value
|
||||
|
||||
# disagreement
|
||||
t1.pier_side = True
|
||||
shipcall.pier_side = True
|
||||
# t1.pier_side = True
|
||||
t2.pier_side = False
|
||||
|
||||
time_objects = [t1, t2]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user