Compare commits

...

42 Commits

Author SHA1 Message Date
7110810c8c fix shipcall query to include times eta/etd 2024-02-05 10:20:31 +01:00
4c73145857 Merge branch 'master' into release/1.1.1 2024-01-16 07:20:06 +01:00
a9f1de2637 Changes for (blue) release version 2024-01-15 18:13:04 +01:00
e659203364 Version bump 1.1.4 -> 1.1.5 2024-01-12 11:33:35 +01:00
3727bf266d Merge branch 'bugfix/tug_times_trouble' into release/1.1.1 2024-01-12 11:27:57 +01:00
3e17983f2d Removed spinner from IMO integerUpDown 2024-01-12 11:26:12 +01:00
126d74ec48 Changed validation rule 002 A-C so that the agency time is set as reference time allowing 15 minute deviation 2024-01-12 11:22:20 +01:00
b44620b0ad Version bump 1.1.3 -> 1.1.4 2024-01-05 10:55:48 +01:00
df5f88d3cf Fixed time comparison validation func. Now compares min/max value of array. 2024-01-05 10:51:13 +01:00
e5f5694d52 fixed filtering by berth id, also including outgoing and shifting moves 2024-01-05 08:09:54 +01:00
96b0d7aecf set appsettings correctly 2023-12-29 15:01:35 +01:00
6952fadbcb Version bump 1.1.2 -> 1.1.3 2023-12-29 14:51:38 +01:00
0f313e2c21 Merge branch 'bugfix/ui_fixes_1.1.2' into release/1.1.1 2023-12-29 14:49:58 +01:00
8d1415e33d Version bump 1.1.1 -> 1.1.2 2023-12-28 11:59:58 +01:00
5f01188541 merged fixes from correction branch 2023-12-28 11:55:03 +01:00
e7936b679c remaining stuff 2023-12-26 17:46:58 +01:00
f76bcf3cbb Changed strings labels and colors for test version 2023-12-26 17:43:54 +01:00
956ba2832a Merge branch 'bugfix/empty_times' 2023-12-01 10:02:40 +02:00
scopesorting
377ec85ce9 more concise evaluation messages for 0001. Adding newlines (works on Windows) when multiple evaluation messages are shown. Properly adding the ShipcallType filters for each rule (whether incoming, outgoing or shifting). Added a regular expression to abbreviate an evaluation message when 512 characters are exceeded. 2023-12-01 09:29:20 +02:00
scopesorting
a0785d012c changing the ParticipantType to an IntFlag, so multiple roles are possible. Adapting every validation rule (0001, 0003, 0004, 0005), which may be affected by this change. Changing the filter for a participant type to properly include the change. Changing the pier_side rule (0006B), which uses the shipcall and times_terminal. New shipcalls should now be evaluated properly, unless no participant is assigned at all. If the ladder case can occur, the validation rules 0001N+0001O will be added (held back for now). 2023-12-01 09:29:01 +02:00
scopesorting
efff2fdf82 removing verbosity in validation rule functions, and returning 'None', when a selected times dataframe is empty. In case of empty results, the function now properly computes the delta towards a query time and returns YELLOW, when a violation is observed. This should finally fix the bugs for 0001 A-M 2023-12-01 09:28:36 +02:00
scopesorting
d6a6cc00ff updating validation rules 0001 A-M. Instead of filtering by times_df (which may not exist), the rules make use of the shipcall_participant_map. When one of the participants in a rule is not assigned, no violation is observed. When there are multiple entries of a participant (due to an input bug), the function still verifies properly. When critical time is observed, and there is not yet an entry for the respective key time, there will be a 'yellow' state. 2023-12-01 09:28:11 +02:00
scopesorting
ecdf66bff2 updating check_time_delta_violation_query_time_to_now: no longer ignoring events of the past (delta<=0) 2023-11-28 16:48:20 +01:00
scopesorting
287224eeb4 ensuring that len(df_times) always works. Preventing 'None' from occuring 2023-11-28 14:49:40 +01:00
scopesorting
210252df22 minot adjustments & refactoring 2023-11-28 14:26:38 +01:00
scopesorting
c064556668 fixing the 'KeyError' when using an empty times dataframe. Returning 'green' 2023-11-28 14:17:07 +01:00
adaef94854 added trace output and fixed a bug when saving shipcalls without times 2023-11-28 11:23:46 +01:00
c3f8759c70 fixed error in publish xml 2023-11-26 18:08:38 +01:00
131528021d set everything to blue, meaning prod version 1.0.0 2023-11-26 17:37:31 +01:00
5b2d6db5d3 set everything to red, meaning test version 1.0.0 2023-11-26 17:05:32 +01:00
902cb9d90d Updated to Version 0.9.6 for public version 2023-11-15 14:59:23 +01:00
e77c9264b9 Merge branch 'release/pub_0.9.4' 2023-10-27 09:05:08 +02:00
79f6f26293 setup client to produce a producion release 2023-10-27 08:45:10 +02:00
1f355203f0 Version bump to 0.9.4.0 2023-10-27 07:41:06 +02:00
2917fb5b2a Merge branch 'release/0.6' 2023-09-21 10:51:37 +02:00
c104fcf9c0 updated test profile 2023-09-21 10:48:16 +02:00
ccf4ed0565 Merge branch 'release/0.5.0' 2023-09-04 07:55:29 +02:00
d0c0a62cfe Merge branch 'release/0.4.0' 2023-09-04 07:55:06 +02:00
ed0a337929 Increased version for release 2023-08-24 08:15:46 +02:00
4dd07da09b Release 0.4.0. 2023-08-21 19:27:14 +02:00
699552e23c Version 0.3.0 Client release 2023-08-18 16:44:06 +02:00
d09ca5bb8a updated version info 2023-08-16 10:27:13 +02:00
20 changed files with 229 additions and 197 deletions

View File

@ -3582,7 +3582,7 @@ namespace BreCalClient.misc.Client
{
Proxy = null;
UserAgent = WebUtility.UrlEncode("OpenAPI-Generator/1.0.0/csharp");
BasePath = "https://brecaldevel.bsmd-emswe.eu";
BasePath = "https://brecal.bsmd-emswe.eu";
DefaultHeaders = new ConcurrentDictionary<string, string>();
ApiKey = new ConcurrentDictionary<string, string>();
ApiKeyPrefix = new ConcurrentDictionary<string, string>();
@ -3590,7 +3590,7 @@ namespace BreCalClient.misc.Client
{
{
new Dictionary<string, object> {
{"url", "https://brecaldevel.bsmd-emswe.eu"},
{"url", "https://brecal.bsmd-emswe.eu"},
{"description", "Test server hosted on vcup"},
}
}
@ -3609,7 +3609,7 @@ namespace BreCalClient.misc.Client
IDictionary<string, string> defaultHeaders,
IDictionary<string, string> apiKey,
IDictionary<string, string> apiKeyPrefix,
string basePath = "https://brecaldevel.bsmd-emswe.eu") : this()
string basePath = "https://brecal.bsmd-emswe.eu") : this()
{
if (string.IsNullOrWhiteSpace(basePath))
throw new ArgumentException("The provided basePath is invalid.", "basePath");

View File

@ -13,7 +13,7 @@ info:
url: "https://www.bsmd.de/license"
servers:
- url: "https://brecaltest.bsmd-emswe.eu/"
- url: "https://brecal.bsmd-emswe.eu/"
description: "Test server hosted on vcup"
paths:

View File

@ -1 +1 @@
1.1.1.0
1.1.5.0

View File

@ -11,16 +11,16 @@
<applicationSettings>
<BreCalClient.Properties.Settings>
<setting name="BG_COLOR" serializeAs="String">
<value>#1D751F</value>
<value>#203864</value>
</setting>
<setting name="APP_TITLE" serializeAs="String">
<value>!!Bremen calling Testversion!!</value>
<value>Bremen calling</value>
</setting>
<setting name="LOGO_IMAGE_URL" serializeAs="String">
<value>https://www.textbausteine.net/</value>
</setting>
<setting name="API_URL" serializeAs="String">
<value>https://brecaldevel.bsmd-emswe.eu</value>
<value>https://brecal.bsmd-emswe.eu</value>
</setting>
</BreCalClient.Properties.Settings>
</applicationSettings>

View File

@ -8,12 +8,12 @@
<SignAssembly>True</SignAssembly>
<StartupObject>BreCalClient.App</StartupObject>
<AssemblyOriginatorKeyFile>..\..\misc\brecal.snk</AssemblyOriginatorKeyFile>
<AssemblyVersion>1.1.1.0</AssemblyVersion>
<FileVersion>1.1.1.0</FileVersion>
<AssemblyVersion>1.1.5.0</AssemblyVersion>
<FileVersion>1.1.5.0</FileVersion>
<Title>Bremen calling client</Title>
<Description>A Windows WPF client for the Bremen calling API.</Description>
<ApplicationIcon>containership.ico</ApplicationIcon>
<AssemblyName>BreCalDevelClient</AssemblyName>
<AssemblyName>BreCalClient</AssemblyName>
</PropertyGroup>
<ItemGroup>

View File

@ -32,7 +32,7 @@
<Label Content="{x:Static p:Resources.textShip}" Grid.Column="0" Grid.Row="0" HorizontalContentAlignment="Right"/>
<ComboBox x:Name="comboBoxShip" Margin="2" Grid.Column="1" Grid.Row="0" SelectionChanged="comboBoxShip_SelectionChanged" SelectedValuePath="Ship.Id" IsEditable="True" IsTextSearchEnabled="True" IsTextSearchCaseSensitive="False" />
<Label Content="IMO" Grid.Column="0" Grid.Row="1" HorizontalContentAlignment="Right"/>
<xctk:IntegerUpDown x:Name="integerUpDownIMO" IsReadOnly="True" Margin="2" Grid.Column="1" Grid.Row="1" />
<xctk:IntegerUpDown x:Name="integerUpDownIMO" IsReadOnly="True" Margin="2" Grid.Column="1" Grid.Row="1" ShowButtonSpinner="False"/>
<Label Content="{x:Static p:Resources.textCallsign}" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Right"/>
<TextBox x:Name="textBoxCallsign" IsReadOnly="True" Grid.Column="1" Grid.Row="2" Margin="2" />
<Label Content="{x:Static p:Resources.textLength}" Grid.Column="0" Grid.Row="3" HorizontalContentAlignment="Right"/>

View File

@ -516,7 +516,8 @@ namespace BreCalClient
if(sfm.Berths.Count > 0 )
{
this._visibleControlModels.RemoveAll(x => !sfm.Berths.Contains((x.Shipcall?.ArrivalBerthId) ?? -1));
this._visibleControlModels.RemoveAll(x => (!sfm.Berths.Contains((x.Shipcall?.ArrivalBerthId) ?? -1) && (x.Shipcall?.Type == (int) Extensions.TypeEnum.Incoming)) ||
(!sfm.Berths.Contains((x.Shipcall?.DepartureBerthId) ?? -1) && (x.Shipcall?.Type != (int) Extensions.TypeEnum.Incoming)));
}
if(sfm.Agencies.Count > 0 )

View File

@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Project>
<PropertyGroup>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.1.1.0</ApplicationVersion>
<ApplicationVersion>1.1.3.0</ApplicationVersion>
<BootstrapperEnabled>True</BootstrapperEnabled>
<Configuration>Debug</Configuration>
<CreateDesktopShortcut>True</CreateDesktopShortcut>

View File

@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Project>
<PropertyGroup>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>6.0</ApplicationVersion>
<ApplicationVersion>1.1.5.*</ApplicationVersion>
<BootstrapperEnabled>False</BootstrapperEnabled>
<Configuration>Release</Configuration>
<CreateWebPageOnPublish>True</CreateWebPageOnPublish>

View File

@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Project>
<PropertyGroup>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.1.0.*</ApplicationVersion>
<ApplicationVersion>1.1.5.*</ApplicationVersion>
<BootstrapperEnabled>True</BootstrapperEnabled>
<Configuration>Debug</Configuration>
<CreateDesktopShortcut>True</CreateDesktopShortcut>

View File

@ -9,38 +9,38 @@
//------------------------------------------------------------------------------
namespace BreCalClient.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("#1D751F")]
[global::System.Configuration.DefaultSettingValueAttribute("#203864")]
public string BG_COLOR {
get {
return ((string)(this["BG_COLOR"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("!!Bremen calling Testversion!!")]
[global::System.Configuration.DefaultSettingValueAttribute("Bremen calling")]
public string APP_TITLE {
get {
return ((string)(this["APP_TITLE"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://www.textbausteine.net/")]
@ -49,7 +49,7 @@ namespace BreCalClient.Properties {
return ((string)(this["LOGO_IMAGE_URL"]));
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
@ -61,16 +61,16 @@ namespace BreCalClient.Properties {
this["FilterCriteria"] = value;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://brecaldevel.bsmd-emswe.eu")]
[global::System.Configuration.DefaultSettingValueAttribute("https://brecal.bsmd-emswe.eu")]
public string API_URL {
get {
return ((string)(this["API_URL"]));
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("800")]
@ -82,7 +82,7 @@ namespace BreCalClient.Properties {
this["Width"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("450")]
@ -94,7 +94,7 @@ namespace BreCalClient.Properties {
this["Height"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -106,7 +106,7 @@ namespace BreCalClient.Properties {
this["Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -118,7 +118,7 @@ namespace BreCalClient.Properties {
this["Top"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
@ -130,7 +130,7 @@ namespace BreCalClient.Properties {
this["W1Left"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]

View File

@ -3,10 +3,10 @@
<Profiles />
<Settings>
<Setting Name="BG_COLOR" Type="System.String" Scope="Application">
<Value Profile="(Default)">#1D751F</Value>
<Value Profile="(Default)">#203864</Value>
</Setting>
<Setting Name="APP_TITLE" Type="System.String" Scope="Application">
<Value Profile="(Default)">!!Bremen calling Testversion!!</Value>
<Value Profile="(Default)">Bremen calling</Value>
</Setting>
<Setting Name="LOGO_IMAGE_URL" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://www.textbausteine.net/</Value>
@ -15,7 +15,7 @@
<Value Profile="(Default)" />
</Setting>
<Setting Name="API_URL" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://brecaldevel.bsmd-emswe.eu</Value>
<Value Profile="(Default)">https://brecal.bsmd-emswe.eu</Value>
</Setting>
<Setting Name="Width" Type="System.Double" Scope="User">
<Value Profile="(Default)">800</Value>

View File

@ -1,12 +1,12 @@
<UserControl x:Class="BreCalClient.ShipcallControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
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=BreCalDevelClient"
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>
@ -45,7 +45,7 @@
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<Image Margin="2" Grid.Column="0" PreviewMouseUp="Image_PreviewMouseUp" x:Name="imageShipcallType" />
<Label Grid.Column="1" FontSize="12" x:Name="labelShipName" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Column="1" FontSize="12" x:Name="labelShipName" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewMouseUp="Image_PreviewMouseUp">
<TextBlock Name="textBlockShipName" />
</Label>
@ -83,23 +83,23 @@
<Viewbox Grid.Row="6" Grid.Column="1" HorizontalAlignment="Left">
<TextBlock x:Name="textBlockBerth" Padding="0" FontWeight="DemiBold" />
</Viewbox>
</Grid>
<Label Grid.Row="0" Grid.Column="1" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="1" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelAgent" PreviewMouseUp="labelAgent_PreviewMouseUp"/>
<Label Grid.Row="0" Grid.Column="2" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="2" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelMooring" PreviewMouseUp="labelMooring_PreviewMouseUp"/>
<Label Grid.Row="0" Grid.Column="3" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="3" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelPortAuthority" PreviewMouseUp="labelPortAuthority_PreviewMouseUp" />
<Label Grid.Row="0" Grid.Column="4" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="4" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelPilot" PreviewMouseUp="labelPilot_PreviewMouseUp"/>
<Label Grid.Row="0" Grid.Column="5" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="5" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelTug" PreviewMouseUp="labelTug_PreviewMouseUp"/>
<Label Grid.Row="0" Grid.Column="6" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
<Label Grid.Row="0" Grid.Column="6" Grid.RowSpan="1" FontSize="12" Content="- / -" Foreground="White" Background="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Name="labelTerminal" PreviewMouseUp="labelTerminal_PreviewMouseUp" />
<!-- AGENCY -->
<Border Grid.Row="2" Grid.Column="1" BorderThickness="1, 0, 0, 0" BorderBrush="{Binding Source={x:Static sets:Settings.Default}, Path=BG_COLOR}" Padding="3,0,0,0">
<Grid Grid.Row="2" Grid.Column="1">
@ -113,7 +113,7 @@
<RowDefinition Height="14" />
<RowDefinition Height=".5*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content = "ETA" x:Name="labelETAETDAgent" Padding="0" VerticalContentAlignment="Center" />
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static p:Resources.textRemarks}" Padding="0" VerticalContentAlignment="Center" FontSize="9"/>
<Label Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Content="{x:Static p:Resources.textBerth}" Padding="0" VerticalContentAlignment="Center" FontSize="9"/>

View File

@ -1,6 +1,6 @@
// Copyright (c) 2023 schick Informatik
// Description: Show general shipcall info
//
//
using BreCalClient.misc.Model;
using log4net;
@ -53,7 +53,7 @@ namespace BreCalClient
/// <summary>
/// this is our datasource
/// </summary>
public ShipcallControlModel? ShipcallControlModel { get; set; }
public ShipcallControlModel? ShipcallControlModel { get; set; }
#endregion
@ -131,7 +131,7 @@ namespace BreCalClient
{
this.labelTerminal.FontWeight = FontWeights.Bold;
this.labelTerminal.Foreground = Brushes.LightYellow;
}
}
else
{
this.labelTerminal.FontWeight = FontWeights.Normal;
@ -210,13 +210,13 @@ namespace BreCalClient
switch (this.ShipcallControlModel?.Shipcall?.Type)
{
case 1: // incoming
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_down_red.png"));
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_down_red.png"));
break;
case 2: // outgoing
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_up_blue.png"));
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_up_blue.png"));
break;
case 3: // shifting
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/arrow_right_green.png"));
this.imageShipcallType.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/arrow_right_green.png"));
break;
default:
break;
@ -225,13 +225,13 @@ namespace BreCalClient
switch(this.ShipcallControlModel?.LightMode)
{
case ShipcallControlModel.TrafficLightMode.GREEN:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/check.png"));
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/check.png"));
break;
case ShipcallControlModel.TrafficLightMode.YELLOW:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/sign_warning.png"));
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/sign_warning.png"));
break;
case ShipcallControlModel.TrafficLightMode.RED:
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalDevelClient;component/Resources/delete2.png"));
this.imageEvaluation.Source = new BitmapImage(new Uri("pack://application:,,,/BreCalClient;component/Resources/delete2.png"));
break;
default:
break;
@ -274,7 +274,7 @@ namespace BreCalClient
this.textBlockBerth.Text = this.ShipcallControlModel?.Berth;
this.textBlockCallsign.Text = this.ShipcallControlModel?.Ship?.Callsign;
if (this.ShipcallControlModel?.Shipcall?.Type == 1)
if (this.ShipcallControlModel?.Shipcall?.Type == 1)
{
this.textBlockETA.Text = this.ShipcallControlModel?.Shipcall?.Eta?.ToString("dd.MM. HH:mm");
}
@ -287,7 +287,7 @@ namespace BreCalClient
this.textBlockIMO.Text = this.ShipcallControlModel?.Ship?.Imo.ToString();
this.textBlockLengthWidth.Text = $"{this.ShipcallControlModel?.Ship?.Length} / {this.ShipcallControlModel?.Ship?.Width}";
// rename labels if this is not an incoming
// rename labels if this is not an incoming
// must be here because there may not be a times record for each participant (yet)
if (this.ShipcallControlModel?.Shipcall?.Type != 1)
@ -301,7 +301,7 @@ namespace BreCalClient
}
if (this.ShipcallControlModel != null)
{
{
Times? agencyTimes = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
if (agencyTimes != null)
@ -314,7 +314,7 @@ namespace BreCalClient
{
this.labelAgencyETAETDValue.Content = agencyTimes.EtdBerth.HasValue ? agencyTimes.EtdBerth.Value.ToString("dd.MM.yyyy HH:mm") : "- / -";
}
}
}
else
{
// no agency times set (yet)
@ -408,7 +408,7 @@ namespace BreCalClient
this.textBlockTerminalRemarks.Text = "";
this.textBlockTerminalBerthRemarks.Text = "";
}
}
this.DataContext = this.ShipcallControlModel;
}
@ -432,7 +432,7 @@ namespace BreCalClient
{
if(App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD))
this.EditRequested?.Invoke(this);
}
}
private void Image_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
@ -440,10 +440,10 @@ namespace BreCalClient
}
private void labelAgent_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
{
Times? times = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.AGENCY);
this.EditAgencyRequested?.Invoke(this, times);
}
this.EditAgencyRequested?.Invoke(this, times);
}
private void labelMooring_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
@ -453,7 +453,7 @@ namespace BreCalClient
private void labelPortAuthority_PreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Times? times = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.PORT_ADMINISTRATION);
Times? times = this.ShipcallControlModel?.GetTimesForParticipantType(Extensions.ParticipantType.PORT_ADMINISTRATION);
this.EditTimesRequested?.Invoke(this, times, Extensions.ParticipantType.PORT_ADMINISTRATION);
}

View File

@ -61,7 +61,7 @@ def create_app(test_config=None):
app.register_blueprint(user.bp)
logging.basicConfig(filename='brecaldevel.log', level=logging.DEBUG, format='%(asctime)s | %(name)s | %(levelname)s | %(message)s')
logging.basicConfig(filename='brecal.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')
@ -73,8 +73,8 @@ def create_app(test_config=None):
return app
__all__ = [
"get_project_root",
"ensure_path",
"get_project_root",
"ensure_path",
"execute_test_with_pytest",
"execute_coverage_test",
"difference_to_then",

View File

@ -19,11 +19,20 @@ def GetShipcalls(options):
pooledConnection = local_db.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"])
query = ("SELECT s.id as id, ship_id, type, eta, voyage, etd, arrival_berth_id, departure_berth_id, tug_required, pilot_required, " +
"flags, s.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, s.created as created, s.modified as modified " +
"FROM shipcall s " +
"LEFT JOIN times t ON t.shipcall_id = s.id AND t.participant_type = 8 " +
"WHERE " +
"(type = 1 AND " +
"((t.id IS NOT NULL AND t.eta_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " +
"(eta >= DATE(NOW() - INTERVAL %d DAY)))) OR " +
"((type = 2 OR type = 3) AND " +
"((t.id IS NOT NULL AND t.etd_berth >= DATE(NOW() - INTERVAL %d DAY)) OR " +
"(etd >= DATE(NOW() - INTERVAL %d DAY)))) " +
"ORDER BY eta") % (options["past_days"], options["past_days"], options["past_days"], options["past_days"])
data = commands.query(query, model=model.Shipcall)
for shipcall in data:

View File

@ -11,7 +11,7 @@ def initPool(instancePath):
try:
global config_path
if(config_path == None):
config_path = os.path.join(instancePath,'../../../secure/connection_data_devel.json');
config_path = os.path.join(instancePath,'../../../secure/connection_data_prod.json');
print (config_path)

View File

@ -60,13 +60,13 @@ class ValidationRuleBaseFunctions():
def describe_error_message(self, key)->str:
"""
Takes any error message, which typically is the validation rule's function name and returns a description of the error.
Takes any error message, which typically is the validation rule's function name and returns a description of the error.
In case that the error code is not defined in self.error_message_dict, return the cryptic error code instead
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)
@ -75,7 +75,7 @@ class ValidationRuleBaseFunctions():
"""
# base function for all validation rules in the group {0001} A-L
measures the time between NOW and query_time.
measures the time between NOW and query_time.
When the query_time lays in the past, the delta is negative
when the query_time lays in the future, the delta is positive
@ -93,11 +93,11 @@ class ValidationRuleBaseFunctions():
# rule is only applicable, when 'key_time' is not defined (neither None, nor pd.NaT)
if (key_time is not None) and (key_time is not pd.NaT):
return False
# when query_time is not valid, the rule cannot be applied
if self.check_is_not_a_time_or_is_none(query_time):
return False
# otherwise, this rule applies and the difference between 'now' and the query time is measured
delta = self.time_logic.time_delta_from_now_to_tgt(tgt_time=query_time, unit="m")
@ -105,7 +105,7 @@ class ValidationRuleBaseFunctions():
# 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:
"""
# base function for all validation rules in the group {0002} A-C
@ -116,11 +116,11 @@ class ValidationRuleBaseFunctions():
No violations are observed, when
- the shipcall belongs to a different type than the rule expects
- there are no matching times for the provided {query} (e.g., "eta_berth")
Instead of comparing each individual result, this function counts the amount of unique instances.
When there is not only one unique value, there are deviating time estimates, and a violation occurs
To reduce the potential of false violations, the agreement is rounded (e.g., by minute).
To reduce the potential of false violations, the agreement is rounded (e.g., by minute).
returns: violation_state (bool)
"""
@ -133,28 +133,50 @@ class ValidationRuleBaseFunctions():
if not self.ignore_port_administration_flag:
participant_types = [ParticipantType.AGENCY.value, ParticipantType.MOORING.value, ParticipantType.PORT_ADMINISTRATION.value, ParticipantType.PILOT.value, ParticipantType.TUG.value]
else:
participant_types = [ParticipantType.AGENCY.value, ParticipantType.MOORING.value, ParticipantType.PORT_ADMINISTRATION.value, ParticipantType.PILOT.value, ParticipantType.TUG.value]
participant_types = [ParticipantType.AGENCY.value, ParticipantType.MOORING.value, ParticipantType.PILOT.value, ParticipantType.TUG.value]
agency_times = df_times.loc[df_times["participant_type"]==ParticipantType.AGENCY.value,:]
df_times = df_times.loc[df_times["participant_type"].isin(participant_types),:]
agency_time = [time_ for time_ in agency_times.loc[:,query].tolist() if isinstance(time_, pd.Timestamp)]
if not len(agency_time):
violation_state = False
return violation_state
# exclude missing entries and consider only pd.Timestamp entries (which ignores pd.NaT/null entries)
estimated_times = [time_ for time_ in df_times.loc[:,query].tolist() if isinstance(time_, pd.Timestamp)] # df_times = df_times.loc[~df_times[query].isnull(),:]
# apply rounding. For example, the agreement of different participants may be required to match minute-wise
# '15min' rounds to 'every 15 minutes'. E.g., '2023-09-22 08:18:49' becomes '2023-09-22 08:15:00'
estimated_times = [time_.round("15min") for time_ in estimated_times]
# when there are no entries left (no entries are provided), skip
if len(estimated_times)==0:
violation_state = False
return violation_state
# this (current) solution compares times to the reference (agency) time and checks if the difference is greater than 15 minutes
violation_state = ((np.max(estimated_times) - agency_time[0]) > pd.Timedelta("15min")) or ((agency_time[0] - np.min(estimated_times)) > pd.Timedelta("15min"))
# this solution to the rule compares all times to each other. When there is a total difference of more than 15 minutes, a violation occurs
# Consequently, it treats all times as equally important
# difference = np.max(estimated_times) - np.min(estimated_times)
# violation_state = difference > pd.Timedelta("15min")
# this solution clamps the times to 15 minute intervals and compares these values. When there is a single time difference, a violation occurs
# the drawback is that in some cases if there is a minimal difference say of 1 minute (:22 and :23 minutes after the hour) the violation is
# triggered even though the times are very close to each other
# apply rounding. For example, the agreement of different participants may be required to match minute-wise
# '15min' rounds to 'every 15 minutes'. E.g., '2023-09-22 08:18:49' becomes '2023-09-22 08:15:00'
# estimated_times = [time_.round("15min") for time_ in estimated_times]
# there should only be one eta_berth, when all participants have provided the same time
# this equates to the same criteria as checking, whether
# times_agency.eta_berth==times_mooring.eta_berth==times_portadministration.eta_berth==times_pilot.eta_berth==times_tug.eta_berth
n_unique_times = len(np.unique(estimated_times))
violation_state = n_unique_times!=1
# n_unique_times = len(np.unique(estimated_times))
# violation_state = n_unique_times!=1
return violation_state
def check_unique_shipcall_counts(self, query:str, times_agency:pd.DataFrame, rounding="min", maximum_threshold=3, all_times_agency=None)->bool:
"""
# base function for all validation rules in the group {0005} A&B
@ -182,13 +204,13 @@ class ValidationRuleBaseFunctions():
class ValidationRuleFunctions(ValidationRuleBaseFunctions):
"""
an accumulation object that makes sure, that any validation rule is translated to a function with default naming convention and
an accumulation object that makes sure, that any validation rule is translated to a function with default naming convention and
return types. Each function should return a ValidationRuleState enumeration object and a description string to which validation rule
the result belongs. These are returned as tuples (ValidationRuleState, validation_name)
Each rule should have the same input arguments (self, shipcall, df_times, *args, **kwargs)
The object makes heavy use of calls from an SQLHandler object, which provides functions for dataframe access and filtering.
each validation_name is generated by calling the function inside a method
validation_name = inspect.currentframe().f_code.co_name # validation_name then returns the name of the method from where 'currentframe()' was called.
@ -199,12 +221,12 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
"""
def __init__(self, sql_handler):
super().__init__(sql_handler)
return
return
def get_validation_rule_functions(self):
"""return a list of all methods in this object, which are all validation rule functions."""
return [self.__getattribute__(mthd_) for mthd_ in dir(self) if ('validation_rule_fct' in mthd_) and (isinstance(self.__getattribute__(mthd_), types.MethodType))]
def validation_rule_fct_missing_time_agency_berth_eta(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-A
@ -213,17 +235,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-A:
- Checks, if times_agency.eta_berth is filled in.
- Checks, if times_agency.eta_berth is filled in.
- Measures the difference between 'now' and 'shipcall.eta'.
"""
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
@ -236,7 +258,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_agency_berth_etd(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-B
@ -245,17 +267,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-B:
- Checks, if times_agency.etd_berth is filled in.
- Checks, if times_agency.etd_berth is filled in.
- Measures the difference between 'now' and 'shipcall.etd'.
"""
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
@ -268,7 +290,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_mooring_berth_eta(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-C
@ -277,12 +299,12 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-C:
- Checks, if times_mooring.eta_berth is filled in.
- Checks, if times_mooring.eta_berth is filled in.
- Measures the difference between 'now' and 'times_agency.eta_berth'.
"""
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:
@ -302,7 +324,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_mooring_berth_etd(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-D
@ -311,12 +333,12 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-D:
- Checks, if times_mooring.etd_berth is filled in.
- Checks, if times_mooring.etd_berth is filled in.
- Measures the difference between 'now' and 'times_agency.etd_berth'.
"""
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:
@ -336,7 +358,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_portadministration_berth_eta(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-F
@ -345,20 +367,20 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-F:
- Checks, if times_port_administration.eta_berth is filled in.
- Checks, if times_port_administration.eta_berth is filled in.
- Measures the difference between 'now' and 'times_agency.eta_berth'.
"""
if self.ignore_port_administration_flag:
return self.get_no_violation_default_output()
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)
@ -373,7 +395,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_portadministration_berth_etd(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-G
@ -382,7 +404,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-G:
- Checks, if times_port_administration.etd_berth is filled in.
- Checks, if times_port_administration.etd_berth is filled in.
- Measures the difference between 'now' and 'times_agency.etd_berth'.
"""
if self.ignore_port_administration_flag:
@ -390,12 +412,12 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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)
@ -411,7 +433,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_pilot_berth_eta(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-H
@ -420,17 +442,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-H:
- Checks, if times_pilot.eta_berth is filled in.
- Checks, if times_pilot.eta_berth is filled in.
- Measures the difference between 'now' and 'times_agency.eta_berth'.
"""
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)
@ -439,13 +461,13 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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 = "validation_rule_fct_missing_time_pilot_berth_eta"
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_pilot_berth_etd(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-I
@ -454,17 +476,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-I:
- Checks, if times_pilot.etd_berth is filled in.
- Checks, if times_pilot.etd_berth is filled in.
- Measures the difference between 'now' and 'times_agency.etd_berth'.
"""
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)
@ -473,13 +495,13 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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 = "validation_rule_fct_missing_time_pilot_berth_etd"
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_tug_berth_eta(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-J
@ -488,17 +510,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-J:
- Checks, if times_tug.eta_berth is filled in.
- Checks, if times_tug.eta_berth is filled in.
- Measures the difference between 'now' and 'times_agency.eta_berth'.
"""
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)
@ -507,13 +529,13 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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 = "validation_rule_fct_missing_time_tug_berth_eta"
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_tug_berth_etd(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-K
@ -522,17 +544,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-K:
- Checks, if times_tug.etd_berth is filled in.
- Checks, if times_tug.etd_berth is filled in.
- Measures the difference between 'now' and 'times_agency.etd_berth'.
"""
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)
@ -541,13 +563,13 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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 = "validation_rule_fct_missing_time_tug_berth_etd"
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_terminal_berth_eta(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-L
@ -556,17 +578,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-L:
- Checks, if times_terminal.operations_start is filled in.
- Checks, if times_terminal.operations_start is filled in.
- Measures the difference between 'now' and 'times_agency.eta_berth'.
"""
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)
@ -575,13 +597,13 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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 = "validation_rule_fct_missing_time_terminal_berth_eta"
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_missing_time_terminal_berth_etd(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0001-M
@ -590,17 +612,17 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
a certain threshold (e.g., 20 hours), a violation occurs
0001-M:
- Checks, if times_terminal.operations_end is filled in.
- Checks, if times_terminal.operations_end is filled in.
- Measures the difference between 'now' and 'times_agency.etd_berth'.
"""
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)
@ -609,13 +631,13 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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 = "validation_rule_fct_missing_time_terminal_berth_etd"
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_shipcall_incoming_participants_disagree_on_eta(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0002-A
@ -624,21 +646,21 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
Filter: only applies to incoming shipcalls
"""
query = "eta_berth"
violation_state = self.check_participants_agree_on_estimated_time(
shipcall = shipcall,
query=query,
df_times=df_times,
violation_state = self.check_participants_agree_on_estimated_time(
shipcall = shipcall,
query=query,
df_times=df_times,
applicable_shipcall_type=ShipcallType.INCOMING
)
if violation_state:
validation_name = "validation_rule_fct_shipcall_incoming_participants_disagree_on_eta"
return (StatusFlags.RED, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_shipcall_outgoing_participants_disagree_on_etd(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0002-B
@ -647,12 +669,12 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
Filter: only applies to outgoing shipcalls
"""
query = "etd_berth"
violation_state = self.check_participants_agree_on_estimated_time(
shipcall = shipcall,
query=query,
df_times=df_times,
violation_state = self.check_participants_agree_on_estimated_time(
shipcall = shipcall,
query=query,
df_times=df_times,
applicable_shipcall_type=ShipcallType.OUTGOING
)
@ -661,7 +683,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.RED, validation_name)
else:
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):
"""
Code: #0002-C
@ -670,21 +692,21 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
Filter: only applies to shifting shipcalls
"""
violation_state_eta = self.check_participants_agree_on_estimated_time(
shipcall = shipcall,
shipcall = shipcall,
query="eta_berth",
df_times=df_times,
query="eta_berth",
df_times=df_times,
applicable_shipcall_type=ShipcallType.SHIFTING
)
violation_state_etd = self.check_participants_agree_on_estimated_time(
shipcall = shipcall,
shipcall = shipcall,
query="etd_berth",
df_times=df_times,
query="etd_berth",
df_times=df_times,
applicable_shipcall_type=ShipcallType.SHIFTING
)
# apply 'eta_berth' check
# apply 'etd_berth'
# violation: if either 'eta_berth' or 'etd_berth' is violated
@ -698,7 +720,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.RED, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_eta_time_not_in_operation_window(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0003-A
@ -710,14 +732,14 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
"""
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"]==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)
@ -738,7 +760,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.RED, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_etd_time_not_in_operation_window(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0003-B
@ -750,14 +772,14 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
"""
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"]==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)
@ -778,7 +800,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.RED, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_eta_time_not_in_tidal_window(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0004-A
@ -790,7 +812,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
"""
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 self.get_no_violation_default_output()
@ -809,7 +831,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.RED, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_etd_time_not_in_tidal_window(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0004-B
@ -821,7 +843,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
"""
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 self.get_no_violation_default_output()
@ -840,7 +862,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.RED, validation_name)
else:
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):
"""
Code: #0005-A
@ -861,7 +883,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
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):
"""
Code: #0005-B
@ -882,7 +904,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_agency_and_terminal_berth_id_disagreement(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0006-A
@ -895,18 +917,18 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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 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 self.get_no_violation_default_output()
if shipcall.type in [ShipcallType.OUTGOING.value, ShipcallType.SHIFTING.value]:
return self.get_no_violation_default_output()
@ -918,7 +940,7 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
return (StatusFlags.YELLOW, validation_name)
else:
return self.get_no_violation_default_output()
def validation_rule_fct_agency_and_terminal_pier_side_disagreement(self, shipcall, df_times, *args, **kwargs):
"""
Code: #0006-B
@ -931,18 +953,18 @@ class ValidationRuleFunctions(ValidationRuleBaseFunctions):
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 (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(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 self.get_no_violation_default_output()

View File

@ -2,7 +2,7 @@ import os
import sys
import logging
sys.path.insert(0, '/var/www/brecal_devel/src/server')
sys.path.insert(0, '/var/www/brecal/src/server')
sys.path.insert(0, '/var/www/venv/lib/python3.10/site-packages/')
import schedule

View File

@ -2,7 +2,7 @@ from setuptools import find_packages, setup
setup(
name='BreCal',
version='1.1.1',
version='1.1.5',
packages=find_packages(),
include_package_data=True,
zip_safe=False,